dev.dpdk.org archive mirror
 help / color / mirror / Atom feed
* [dpdk-dev]  [PATCH v1 00/26] graph: introduce graph subsystem
@ 2020-03-18 21:35 jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 01/26] graph: define the public API for graph support jerinj
                   ` (26 more replies)
  0 siblings, 27 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

It is the v1 version of the DPDK graph support based on the following
RFC http://patches.dpdk.org/cover/65432/

This patch set contains an implementation of graph architecture for
packet processing using DPDK primitives.

Using graph traversal for packet processing is a proven architecture
that has been implemented in various open source libraries.

Graph architecture for packet processing enables abstracting the data
processing functions as “nodes” and “links” them together to create a
complex “graph” to create reusable/modular data processing functions. 

The patchset further includes performance enhancements and modularity
to the DPDK as discussed in more detail below.

RFC..v1:
--------

1) Split the patch to more logical ones for review.
2) Added doxygen comments for the API
3) Code cleanup
4) Additional performance improvements.
Delta between l3fwd and l3fwd-graph is negligible now.
(~1%) on octeontx2.
5) Added SIMD routines for x86 in additional to arm64.

Pending items (Will be addressed in v2)
-------------------------------------------------
1) Add documentation as a patch for programming guide and l3fwd-graph
user guide.

Addional nodes planned for v20.08
----------------------------------
1) Packet classification node
2) Support for IPV6 LPM node


This patchset contains
-----------------------------
1) The API definition to "create" nodes and "link" together to create a
"graph" for packet processing. See, lib/librte_graph/rte_graph.h  

2) The Fast path API definition for the graph walker and enqueue
function used by the workers. See, lib/librte_graph/rte_graph_worker.h

3) Optimized SW implementation for (1) and (2). See, lib/librte_graph/

4) Test case to verify the graph infrastructure functionality
See, app/test/test_graph.c
 
5) Performance test cases to evaluate the cost of graph walker and nodes
enqueue fast-path function for various combinations.

See app/test/test_graph_perf.c

6) Packet processing nodes(Null, Rx, Tx, Pkt drop, IPV4 rewrite, IPv4
lookup)
using graph infrastructure. See lib/librte_node/*

7) An example application to showcase l3fwd
(functionality same as existing examples/l3fwd) using graph
infrastructure and use packets processing nodes (item (6)). See examples/l3fwd-graph/.

Performance
-----------
1) Graph walk and node enqueue overhead can be tested with performance
test case application [1]
# If all packets go from a node to another node (we call it as
# "homerun") then it will be just a pointer swap for a burst of packets.
# In the worst case, a couple of handful cycles to move an object from a
node to another node.

2) Performance comparison with existing l3fwd (The complete static code
with out any nodes) vs modular l3fwd-graph with 5 nodes
(ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
Here is graphical representation of the l3fwd-graph as Graphviz dot
file: 
http://bit.ly/39UPPGm

# l3fwd-graph performance is -1.2% wrt static l3fwd.

# We have simulated the similar test with existing librte_pipeline
# application [4].
ip_pipline application is -48.62% wrt static l3fwd.

The above results are on octeontx2. It may vary on other platforms.
The platforms with higher L1 and L2 caches will have further better
performance.


Tested architectures:
--------------------
1) AArch64
2) X86


Graph library Features
----------------------
1) Nodes as plugins
2) Support for out of tree nodes
3) Multi-process support.
4) Low overhead graph walk and node enqueue
5) Low overhead statistics collection infrastructure
6) Support to export the graph as a Graphviz dot file.
See rte_graph_export()
Example of exported graph: http://bit.ly/2PqbqOy
7) Allow having another graph walk implementation
in the future by segregating the fast path and slow path code.


Advantages of Graph architecture:
---------------------------------

1) Memory latency is the enemy for high-speed packet processing,
moving the similar packet processing code to a node will reduce
the I cache and D caches misses.
2) Exploits the probability that most packets will follow the same nodes
in the graph.
3) Allow SIMD instructions for packet processing of the node.
4) The modular scheme allows having reusable nodes for the consumers.
5) The modular scheme allows us to abstract the vendor HW specific
optimizations as a node.
               

Why Graph architecture
-----------------------
1) We believe, Graph architecture provides the best performance for 
reusable/modular packet processing framework.
Since DPDK does not have it, it is good to have it in DPDK.

2) Based on our experience, NPU HW accelerates are so different than one
vendor 
to another vendor. Going forward, We believe, API abstraction may not be
enough
abstract the difference in HW. The Vendor-specific nodes can abstract
the HW
differences and reuse generic the nodes as needed.
This would help both the silicon vendors and DPDK end users.

3) The framework enables the protocol stack as use native mbuf for
graph processing to avoid any conversion between the formats for
better performance.

4) DPDK becomes the "goto library" for userspace HW acceleration.
It is good to have native Graph packet processing library in DPDK.

5) Obviously, Our customers are interested in Graph library in DPDK :-)

Identified tweaking for better performance on different targets
---------------------------------------------------------------
1) Test with various burst size values (256, 128, 64, 32) using
CONFIG_RTE_GRAPH_BURST_SIZE config option.
Based on our testing, on x86 and arm64 servers, The sweet spot is 256
burst size.
While on arm64 embedded SoCs, it is either 64 or 128.

2) Disable node statistics (use CONFIG_RTE_LIBRTE_GRAPH_STATS config
option)
if not needed.

3) Use arm64 optimized memory copy for arm64 architecture by
selecting CONFIG_RTE_ARCH_ARM64_MEMCPY. 

Commands to run tests
---------------------

[1] 
perf test:
echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[2]
functionality test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[3]
l3fwd-graph:
./l3fwd-graph -c 0x100  -- -p 0x3 --config="(0, 0, 8)" -P

[4]
# ./ip_pipeline --c 0xff0000 -- -s route.cli

Route.cli: (Copy paste to the shell to avoid dos format issues)

https://pastebin.com/raw/B4Ktx7TT


 
Programming guide and API walk-through
--------------------------------------
# Anatomy of Node:
~~~~~~~~~~~~~~~~~
See the
https://github.com/jerinjacobk/share/blob/master/Anatomy_of_a_node.svg

The above diagram depicts the anatomy of a node.
The node is the basic building block of the graph framework.

A node consists of:
a) process():

The callback function will be invoked by worker thread using
rte_graph_walk() function when there is data to be processed by the
node.
A graph node process the function using process() and enqueue to next
downstream node using rte_node_enqueue*() function.

b) Context memory:  

It is memory allocated by the library to store the node-specific context
information. which will be used by process(), init(), fini() callbacks.

c) init():

The callback function which will be invoked by rte_graph_create() on
when a node 
gets attached to a graph.

d) fini():

The callback function which will be invoked by rte_graph_destroy() on
when a node 
gets detached to a graph.


e) Node name:

It is the name of the node. When a node registers to graph library, the
library 
gives the ID as rte_node_t type. Both ID or Name shall be used lookup
the node.
rte_node_from_name(), rte_node_id_to_name() are the node lookup
functions.

f) nb_edges:

Number of downstream nodes connected to this node. The next_nodes[]
stores the
downstream nodes objects. rte_node_edge_update() and
rte_node_edge_shrink()
functions shall be used to update the next_node[] objects. Consumers of
the node
APIs are free to update the next_node[] objects till rte_graph_create()
invoked.

g) next_node[]:

The dynamic array to store the downstream nodes connected to this node.


# Node creation and registration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
a) Node implementer creates the node by implementing ops and attributes
of
'struct rte_node_register'
b) The library registers the node by invoking RTE_NODE_REGISTER on
library load
using the constructor scheme.
The constructor scheme used here to support multi-process.


# Link the Nodes to create the graph topology
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See the
https://github.com/jerinjacobk/share/blob/master/Link_the_nodes.svg

The above diagram shows a graph topology after linking the N nodes.

Once nodes are available to the program, Application or node public API
functions
can links them together to create a complex packet processing graph.

There are multiple different types of strategies to link the nodes.

Method a) Provide the next_nodes[] at the node registration time.
See  'struct rte_node_register::nb_edges'. This is a use case to address
the static
node scheme where one knows upfront the next_nodes[] of the node.

Method b) Use rte_node_edge_get(), rte_node_edge_update(),
rte_node_edge_shrink() to
Update the next_nodes[] links for the node dynamically.

Method c) Use rte_node_clone() to clone a already existing node.
When rte_node_clone() invoked, The library, would clone all the
attributes
of the node and creates a new one. The name for cloned node shall be
"parent_node_name-user_provided_name". This method enables the use case
of Rx and Tx
nodes where multiple of those nodes need to be cloned based on the
number of CPU
available in the system. The cloned nodes will be identical, except the
"context memory".
Context memory will have information of port, queue pair incase of Rx
and Tx ethdev nodes.
 
# Create the graph object
~~~~~~~~~~~~~~~~~~~~~~~~~
Now that the nodes are linked, Its time to create a graph by including
the required nodes. The application can provide a set of node patterns
to
form a graph object.
The fnmatch() API used underneath for the pattern matching to include
the required nodes.

The rte_graph_create() API shall be used to create the graph.

Example of a graph object creation:

{"ethdev_rx_0_0", ipv4-*, ethdev_tx_0_*"}

In the above example, A graph object will be created with ethdev Rx
node of port 0 and queue 0, all ipv4* nodes in the system,
and ethdev tx node of port 0 with all queues.


# Multi core graph processing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the current graph library implementation, specifically,
rte_graph_walk() and rte_node_enqueue* fast path API functions
are designed to work on single-core to have better performance.
The fast path API works on graph object, So the multi-core graph 
processing strategy would be to create graph object PER WORKER.
 

# In fast path:
~~~~~~~~~~~~~~~

Typical fast-path code looks like below, where the application
gets the fast-path graph object through rte_graph_lookup() 
on the worker thread and run the rte_graph_walk() in a tight loop.

struct rte_graph *graph = rte_graph_lookup("worker0");

while (!done) {
    rte_graph_walk(graph);
}

# Context update when graph walk in action
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The fast-path object for the node is `struct rte_node`. 

It may be possible that in slow-path or after the graph walk-in action,
the user needs to update the context of the node hence access to 
struct rte_node * memory.

rte_graph_foreach_node(), rte_graph_node_get(),
rte_graph_node_get_by_name()
APIs can be used to to get the struct rte_node*.
rte_graph_foreach_node() iterator
function works on struct rte_graph * fast-path graph object while others
works on graph ID or name.


# Get the node statistics using graph cluster
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The user may need to know the aggregate stats of the node across
multiple graph objects. Especially the situation where each
graph object bound to a worker thread.

Introduced a graph cluster object for statistics.
rte_graph_cluster_stats_create()
shall be used for creating a graph cluster with multiple graph objects
and
rte_graph_cluster_stats_get() to get the aggregate node statistics.

An example statistics output from rte_graph_cluster_stats_get()

+-----------+------------+-------------+---------------+------------+---------------+-----------+
|Node       |calls       |objs         |realloc_count  |objs/call
|objs/sec(10E6) |cycles/call|
+------------------------+-------------+---------------+------------+---------------+-----------+
|node0      |12977424    |3322220544   |5              |256.000
|3047.151872    |20.0000    |
|node1      |12977653    |3322279168   |0              |256.000
|3047.210496    |17.0000    |
|node2      |12977696    |3322290176   |0              |256.000
|3047.221504    |17.0000    |
|node3      |12977734    |3322299904   |0              |256.000
|3047.231232    |17.0000    |
|node4      |12977784    |3322312704   |1              |256.000
|3047.243776    |17.0000    |
|node5      |12977825    |3322323200   |0              |256.000
|3047.254528    |17.0000    |
+-----------+------------+-------------+---------------+------------+---------------+-----------+

# Node writing guide lines
~~~~~~~~~~~~~~~~~~~~~~~~~~

The process() function of a node is fast-path function and that needs to
be written
carefully to achieve max performance.

Broadly speaking, there are two different types of nodes.

1) First kind of nodes are those that have a fixed next_nodes[] for the
complete burst (like ethdev_rx, ethdev_tx) and it is simple to write.
Process() function can move the obj burst to the next node either using
rte_node_next_stream_move() or using rte_node_next_stream_get() and
rte_node_next_stream_put().
   
   
2) The second kind of such node is `intermediate nodes` that decide what
is the next_node[]
to send to on a per-packet basis. In these nodes,

a) Firstly, there has to be the best possible packet processing logic.

b) Secondly, each packet needs to be queued to its next node.
This can be done using rte_node_enqueue_[x1|x2|x4]() api's if they are
to single next or
rte_node_enqueue_next() that takes array of nexts. 

In scenario where multiple intermediate nodes are present but most of
the time  each node using same next node for all its packets, cost of moving every
pointer from current node's stream to next node's stream could be avoided.
This is called home run and rte_node_next_stream_move() could be used to 
just move stream from current node to next node with least number of
cycles. 
Since this can be avoided only in the case where all the packets are
destined to the same next node, node implementation should be also having worst
case handling where every packet could be going to different next node.

Example of intermediate node implementation with home run:
a) Start with speculation that next_node = ctx->next_node.
This could be the next_node application used in the previous function
call of this node.
b) Get the next_node stream array with required space using
   rte_node_next_stream_get(next_node, space)
c) while n_left_from > 0 // Pkts left to be sent
prefetch next pkt_set and process current pkt_set to find their next node
d) if all the next nodes of the current pkt_set match speculated next
node, just count them as successfully speculated('last_spec') till now
and continue the loop without actually moving them to the next node.
   else if there is a mismatch,
       copy all the pkt_set pointers that were 'last_spec' and move 
	   the current pkt_set to their respective next's nodes using
       rte_enqueue_next_x1(). Also one of the next_node can be updated
as
       speculated next_node if it is more probable. 
	   Finally reset 'last_spec' to zero.
e) if n_left_from != 0
      goto c) to process remaining pkts.
f) if last_spec == nb_objs,
      All the objects passed were successfully speculated to single next
node.
	  So, the current stream can be moved to next node using 
	  rte_node_next_stream_move(node, next_node). This is home run 
	  where memcpy of buffer pointers to next node is avoided.
g) Update the ctx->next_node with more probable next node.

# In-tree node documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
a) librte_node/ethdev_rx.c:
    This node does rte_eth_rx_burst() into stream buffer passed to it
	(src node stream) and does rte_node_next_stream_move() only when 
	there are packets received. Each rte_node works on only on
    one rx port and queue that it gets from node->context.
    For each (port X, rx_queue Y), a rte_node is cloned from 
	ethdev_rx_base_node as "ethdev_rx-X-Y" in rte_node_eth_config() 
	along with updating node->context. Each graph needs to be associated 
	with a unique rte_node for a (port, rx_queue).

b) librte_node/ethdev_tx.c:
    This node does rte_eth_tx_burst() for a burst of objs received by
it.
    It sends the burst to a fixed Tx Port and Queue information from
    node->context. For each (port X), this rte_node is cloned from
    ethdev_tx_node_base as "ethdev_tx-X" in rte_node_eth_config()
    along with updating node->context.
	Since each graph doesn't need more than one Txq, per port, 
	a Txq is assigned based on graph id to each rte_node instance.
	Each graph needs to be associated with a rte_node for each
(port).

c) librte_node/pkt_drop.c:
    This node frees all the objects passed to it considering them as
	rte_mbufs that need to be freed.

d) librte_node/ip4_lookup.c:
    This node is an intermediate node that does lpm lookup for the
    receive ipv4 packets and the result determines each packets next node.
      a) On successful lpm lookup, the result contains the nex_node id
         and next-hop id with which the packet needs to be further
         processed.
      b) On lpm lookup failure, objects are redirected to pkt_drop node.
      rte_node_ip4_route_add() is control path API to add ipv4 routes.
      To achieve home run, we use rte_node_stream_move() as mentioned in
      above sections.

e) librte_node/ip4_rewrite.c:
      This node gets packets from ip4_lookup node with next-hop id for
      each packet is embedded in rte_node_mbuf_priv1(mbuf)->nh. This id is
      used to determine the L2 header to be written to the pkt before sending
      the pkt out to a particular ethdev_tx node.
      rte_node_ip4_rewrite_add() is control path API to add next-hop info.

f) librte_node/null.c:
      This is null node that just ignores the set of objects passed to
it and reports that all are processed.

Jerin Jacob (12):
  graph: define the public API for graph support
  graph: implement node registration
  graph: implement node operations
  graph: implement node debug routines
  graph: implement internal graph operation helpers
  graph: populate fastpath memory for graph reel
  graph: implement create and destroy APIs
  graph: implement graph operation APIs
  graph: implement Graphviz export
  graph: implement debug routines
  graph: implement stats support
  graph: implement fastpath API routines

Kiran Kumar K (2):
  graph: add unit test case
  node: add ipv4 rewrite node

Nithin Dabilpuram (10):
  node: add log infra and null node
  node: add ethdev Rx node
  node: add ethdev Tx node
  node: add ethdev Rx and Tx node ctrl API
  node: ipv4 lookup for arm64
  node: add ipv4 rewrite and lookup ctrl API
  node: add pkt drop node
  l3fwd-graph: add graph based l3fwd skeleton
  l3fwd-graph: add ethdev configuration changes
  l3fwd-graph: add graph config and main loop

Pavan Nikhilesh (2):
  graph: add performance testcase
  node: ipv4 lookup for x86

 MAINTAINERS                            |   13 +
 app/test/Makefile                      |    5 +
 app/test/meson.build                   |   11 +-
 app/test/test_graph.c                  |  820 +++++++++++++++++
 app/test/test_graph_perf.c             | 1057 ++++++++++++++++++++++
 config/common_base                     |   12 +
 config/rte_config.h                    |    4 +
 doc/api/doxy-api-index.md              |    5 +
 doc/api/doxy-api.conf.in               |    2 +
 examples/Makefile                      |    3 +
 examples/l3fwd-graph/Makefile          |   58 ++
 examples/l3fwd-graph/main.c            | 1113 ++++++++++++++++++++++++
 examples/l3fwd-graph/meson.build       |   13 +
 examples/meson.build                   |    6 +-
 lib/Makefile                           |    6 +
 lib/librte_graph/Makefile              |   28 +
 lib/librte_graph/graph.c               |  590 +++++++++++++
 lib/librte_graph/graph_debug.c         |   84 ++
 lib/librte_graph/graph_ops.c           |  169 ++++
 lib/librte_graph/graph_populate.c      |  234 +++++
 lib/librte_graph/graph_private.h       |  346 ++++++++
 lib/librte_graph/graph_stats.c         |  406 +++++++++
 lib/librte_graph/meson.build           |   11 +
 lib/librte_graph/node.c                |  421 +++++++++
 lib/librte_graph/rte_graph.h           |  786 +++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   47 +
 lib/librte_graph/rte_graph_worker.h    |  541 ++++++++++++
 lib/librte_node/Makefile               |   30 +
 lib/librte_node/ethdev_ctrl.c          |  115 +++
 lib/librte_node/ethdev_rx.c            |  221 +++++
 lib/librte_node/ethdev_rx_priv.h       |   81 ++
 lib/librte_node/ethdev_tx.c            |   86 ++
 lib/librte_node/ethdev_tx_priv.h       |   62 ++
 lib/librte_node/ip4_lookup.c           |  631 ++++++++++++++
 lib/librte_node/ip4_rewrite.c          |  325 +++++++
 lib/librte_node/ip4_rewrite_priv.h     |   77 ++
 lib/librte_node/log.c                  |   14 +
 lib/librte_node/meson.build            |    8 +
 lib/librte_node/node_private.h         |   96 ++
 lib/librte_node/null.c                 |   23 +
 lib/librte_node/pkt_drop.c             |   26 +
 lib/librte_node/rte_node_eth_api.h     |   70 ++
 lib/librte_node/rte_node_ip4_api.h     |   87 ++
 lib/librte_node/rte_node_version.map   |    9 +
 lib/meson.build                        |    5 +-
 meson.build                            |    1 +
 mk/rte.app.mk                          |    2 +
 47 files changed, 8755 insertions(+), 5 deletions(-)
 create mode 100644 app/test/test_graph.c
 create mode 100644 app/test/test_graph_perf.c
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/graph_debug.c
 create mode 100644 lib/librte_graph/graph_ops.c
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/graph_stats.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/node.c
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map
 create mode 100644 lib/librte_graph/rte_graph_worker.h
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/pkt_drop.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h
 create mode 100644 lib/librte_node/rte_node_ip4_api.h
 create mode 100644 lib/librte_node/rte_node_version.map

-- 
2.25.1


^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 01/26] graph: define the public API for graph support
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 02/26] graph: implement node registration jerinj
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Thomas Monjalon, Bruce Richardson, John McNamara,
	Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, david.marchand, mdr, mattias.ronnblom, pbhagavatula, ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Graph architecture abstracts the data processing functions as
"node" and "link" them together to create a complex "graph" to enable
reusable/modular data processing functions.

These APIs enables graph framework operations such as create, lookup,
dump and destroy on graph and node operations such as clone,
edge update, and edge shrink, etc. The API also allows creating the
stats cluster to monitor per graph and per node stats.

This patch defines the public API for graph support.
This patch also adds support for the build infrastructure and
update the MAINTAINERS file for the graph subsystem.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                            |   5 +
 config/common_base                     |   7 +
 config/rte_config.h                    |   4 +
 doc/api/doxy-api-index.md              |   1 +
 doc/api/doxy-api.conf.in               |   1 +
 lib/Makefile                           |   3 +
 lib/librte_graph/Makefile              |  22 +
 lib/librte_graph/graph.c               |   5 +
 lib/librte_graph/meson.build           |  11 +
 lib/librte_graph/rte_graph.h           | 786 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   3 +
 lib/meson.build                        |   2 +-
 mk/rte.app.mk                          |   1 +
 13 files changed, 850 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index c3785554f..32d0ea032 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1468,6 +1468,11 @@ F: examples/bpf/
 F: app/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Graph - EXPERIMENTAL
+M: Jerin Jacob <jerinj@marvell.com>
+M: Kiran Kumar K <kirankumark@marvell.com>
+F: lib/librte_graph/
+
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index 7ca2f28b1..04a96aef5 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1075,6 +1075,13 @@ CONFIG_RTE_LIBRTE_BPF_ELF=n
 #
 CONFIG_RTE_LIBRTE_IPSEC=y
 
+#
+# Compile librte_graph
+#
+CONFIG_RTE_LIBRTE_GRAPH=y
+CONFIG_RTE_GRAPH_BURST_SIZE=256
+CONFIG_RTE_LIBRTE_GRAPH_STATS=y
+
 #
 # Compile the test application
 #
diff --git a/config/rte_config.h b/config/rte_config.h
index d30786bc0..e9201fd46 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -98,6 +98,10 @@
 /* KNI defines */
 #define RTE_KNI_PREEMPT_DEFAULT 1
 
+/* rte_graph defines */
+#define RTE_GRAPH_BURST_SIZE 256
+#define RTE_LIBRTE_GRAPH_STATS 1
+
 /****** driver defines ********/
 
 /* QuickAssist device */
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index dff496be0..5cc50f750 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -159,6 +159,7 @@ The public API headers are grouped by topics:
   * [pipeline]         (@ref rte_pipeline.h)
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
+  * [graph]            (@ref rte_graph.h):
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 1c4392eec..759a7213e 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -33,6 +33,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_eventdev \
                           @TOPDIR@/lib/librte_fib \
                           @TOPDIR@/lib/librte_flow_classify \
+                          @TOPDIR@/lib/librte_graph \
                           @TOPDIR@/lib/librte_gro \
                           @TOPDIR@/lib/librte_gso \
                           @TOPDIR@/lib/librte_hash \
diff --git a/lib/Makefile b/lib/Makefile
index 46b91ae1a..1f572b659 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -119,6 +119,9 @@ DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
 DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu
 DEPDIRS-librte_rcu := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
+DEPDIRS-librte_graph := librte_eal
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
new file mode 100644
index 000000000..26fe514f3
--- /dev/null
+++ b/lib/librte_graph/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_graph.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal
+
+EXPORT_MAP := rte_graph_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
new file mode 100644
index 000000000..a55bf443a
--- /dev/null
+++ b/lib/librte_graph/graph.c
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "rte_graph.h"
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
new file mode 100644
index 000000000..455cf2ba5
--- /dev/null
+++ b/lib/librte_graph/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+name = 'graph'
+
+sources = files('graph.c')
+headers = files('rte_graph.h')
+allow_experimental_apis = true
+
+deps += ['eal']
diff --git a/lib/librte_graph/rte_graph.h b/lib/librte_graph/rte_graph.h
new file mode 100644
index 000000000..4bcf0a6e5
--- /dev/null
+++ b/lib/librte_graph/rte_graph.h
@@ -0,0 +1,786 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_H_
+#define _RTE_GRAPH_H_
+
+/**
+ * @file rte_graph.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph architecture abstracts the data processing functions as
+ * "node" and "link" them together to create a complex "graph" to enable
+ * reusable/modular data processing functions.
+ *
+ * This API enables graph framework operations such as create, lookup,
+ * dump and destroy on graph and node operations such as clone,
+ * edge update, and edge shrink, etc. The API also allows to create the stats
+ * cluster to monitor per graph and per node stats.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_GRAPH_NAMESIZE 64 /**< Max length of graph name. */
+#define RTE_NODE_NAMESIZE 64  /**< Max length of node name. */
+#define RTE_GRAPH_OFF_INVALID UINT32_MAX /**< Invalid graph offset. */
+#define RTE_NODE_ID_INVALID UINT32_MAX   /**< Invalid node id. */
+#define RTE_EDGE_ID_INVALID UINT16_MAX   /**< Invalid edge id. */
+#define RTE_GRAPH_ID_INVALID UINT16_MAX  /**< Invalid graph id. */
+#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL /**< Graph fence data. */
+
+typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
+typedef uint32_t rte_node_t;       /**< Node id type. */
+typedef uint16_t rte_edge_t;       /**< Edge id type. */
+typedef uint16_t rte_graph_t;      /**< Graph id type. */
+
+/** Burst size in terms of log2 */
+#if RTE_GRAPH_BURST_SIZE == 1
+#define RTE_GRAPH_BURST_SIZE_LOG2 0  /**< Object burst size of 1. */
+#elif RTE_GRAPH_BURST_SIZE == 2
+#define RTE_GRAPH_BURST_SIZE_LOG2 1  /**< Object burst size of 2. */
+#elif RTE_GRAPH_BURST_SIZE == 4
+#define RTE_GRAPH_BURST_SIZE_LOG2 2  /**< Object burst size of 4. */
+#elif RTE_GRAPH_BURST_SIZE == 8
+#define RTE_GRAPH_BURST_SIZE_LOG2 3  /**< Object burst size of 8. */
+#elif RTE_GRAPH_BURST_SIZE == 16
+#define RTE_GRAPH_BURST_SIZE_LOG2 4  /**< Object burst size of 16. */
+#elif RTE_GRAPH_BURST_SIZE == 32
+#define RTE_GRAPH_BURST_SIZE_LOG2 5  /**< Object burst size of 32. */
+#elif RTE_GRAPH_BURST_SIZE == 64
+#define RTE_GRAPH_BURST_SIZE_LOG2 6  /**< Object burst size of 64. */
+#elif RTE_GRAPH_BURST_SIZE == 128
+#define RTE_GRAPH_BURST_SIZE_LOG2 7  /**< Object burst size of 128. */
+#elif RTE_GRAPH_BURST_SIZE == 256
+#define RTE_GRAPH_BURST_SIZE_LOG2 8  /**< Object burst size of 256. */
+#else
+#error "Unsupported burst size"
+#endif
+
+/* Forward declaration */
+struct rte_node;  /**< Node data */
+struct rte_graph; /**< Graph data */
+struct rte_graph_cluster_stats;      /**< Stats for Cluster of graphs */
+struct rte_graph_cluster_node_stats; /**< Node stats within cluster of graphs */
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node process function.
+ *
+ * The function invoked when the worker thread walks on nodes using
+ * rte_graph_walk().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param objs
+ *   Pointer to an array of objects to be processed.
+ * @param nb_objs
+ *   Number of objects in the array.
+ *
+ * @return
+ *   Number of objects processed.
+ *
+ * @see rte_graph_walk()
+ *
+ */
+typedef uint16_t (*rte_node_process_t)(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node initialization function.
+ *
+ * The function invoked when the user creates the graph using rte_graph_create()
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ *
+ * @see rte_graph_create()
+ */
+typedef int (*rte_node_init_t)(const struct rte_graph *graph,
+			       struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node finalization function.
+ *
+ * The function invoked when the user destroys the graph using
+ * rte_graph_destroy().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @see rte_graph_destroy()
+ */
+typedef void (*rte_node_fini_t)(const struct rte_graph *graph,
+				struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph cluster stats callback.
+ *
+ * @param is_first
+ *   Flag to denote that stats are of the first node.
+ * @param is_last
+ *   Flag to denote that stats are of the last node.
+ * @param cookie
+ *   Cookie supplied during stats creation.
+ * @param stats
+ *   Node cluster stats data.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ */
+typedef int (*rte_graph_cluster_stats_cb_t)(bool is_first, bool is_last,
+	     void *cookie, const struct rte_graph_cluster_node_stats *stats);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for creating the graph.
+ *
+ * @see rte_graph_create()
+ */
+struct rte_graph_param {
+	int socket_id; /**< Socket id where memory is allocated. */
+	uint16_t nb_node_patterns;  /**< Number of node patterns. */
+	const char **node_patterns;
+	/**< Array of node patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for graph cluster stats create.
+ *
+ * @see rte_graph_cluster_stats_create()
+ */
+struct rte_graph_cluster_stats_param {
+	int socket_id;
+	/**< Socket id where memory is allocated */
+	rte_graph_cluster_stats_cb_t fn;
+	/**< Stats print callback function. NULL value allowed, in that case,
+	 *   default print stat function used.
+	 */
+	RTE_STD_C11
+	union {
+		void *cookie;
+		FILE *f; /**< File pointer to dump the stats when fn == NULL. */
+	};
+	uint16_t nb_graph_patterns;  /**< Number of graph patterns. */
+	const char **graph_patterns;
+	/**< Array of graph patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node cluster stats data structure.
+ *
+ * @see struct rte_graph_cluster_stats_param::fn
+ */
+struct rte_graph_cluster_node_stats {
+	uint64_t ts;	    /**< Current timestamp. */
+	uint64_t calls;	    /**< Current number of calls made. */
+	uint64_t objs;      /**< Current number of objs processed. */
+	uint64_t cycles;    /**< Current number of cycles. */
+
+	uint64_t prev_ts;	/**< Previous call timestamp. */
+	uint64_t prev_calls;	/**< Previous number of calls. */
+	uint64_t prev_objs;	/**< Previous number of processed objs. */
+	uint64_t prev_cycles;	/**< Previous number of cycles. */
+
+	uint64_t realloc_count; /**< Realloc count. */
+
+	rte_node_t id;	/**< Node identifier of stats. */
+	uint64_t hz;	/**< Cycles per seconds. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+} __rte_cache_aligned;
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure defines the node registration parameters.
+ *
+ * @see __rte_node_register(), RTE_NODE_REGISTER()
+ */
+struct rte_node_register {
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
+	rte_node_process_t process; /**< Node process function. */
+	rte_node_init_t init;       /**< Node init function. */
+	rte_node_fini_t fini;       /**< Node fini function. */
+	rte_node_t id;		    /**< Node Identifier. */
+	rte_node_t parent_id;       /**< Identifier of parent node. */
+	rte_edge_t nb_edges;        /**< Number of edges from this node. */
+	const char *next_nodes[];   /**< Names of next nodes. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create Graph.
+ *
+ * Create memory reel, detect loops and find isolated nodes.
+ *
+ * @param name
+ *   Unique name for this graph.
+ * @param prm
+ *   Graph parameter, includes node names and count to be included
+ *   in this graph.
+ *
+ * @return
+ *   Unique graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy Graph.
+ *
+ * Free Graph memory reel.
+ *
+ * @param name
+ *   Name of the graph to destroy.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_destroy(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph id from graph name.
+ *
+ * @param name
+ *   Name of the graph to get id.
+ *
+ * @return
+ *   Graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph name from graph id.
+ *
+ * @param id
+ *   id of the graph to get name.
+ *
+ * @return
+ *   Graph name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_graph_id_to_name(rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Export the graph as graph viz dot file
+ *
+ * @param name
+ *   Name of the graph to export.
+ * @param f
+ *   File pointer to export the graph.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_export(const char *name, FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph object from its name.
+ *
+ * Typical usage of this API to get graph objects in the worker thread and
+ * followed calling rte_graph_walk() in a loop.
+ *
+ * @param name
+ *   Name of the graph.
+ *
+ * @return
+ *   Graph pointer on success, NULL otherwise.
+ *
+ * @see rte_graph_walk()
+ */
+__rte_experimental
+struct rte_graph *rte_graph_lookup(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum number of graph available.
+ *
+ * @return
+ *   Maximum graph count on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump the graph information to file.
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param id
+ *   Graph id to get graph info.
+ */
+__rte_experimental
+void rte_graph_dump(FILE *f, rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all graphs information to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ */
+__rte_experimental
+void rte_graph_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump graph information along with node info to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param graph
+ *   Graph pointer to get graph info.
+ * @param all
+ *   true to dump nodes in the graph.
+ */
+__rte_experimental
+void rte_graph_obj_dump(FILE *f, struct rte_graph *graph, bool all);
+
+/** Macro to browse rte_node object after the graph creation */
+#define rte_graph_foreach_node(count, off, graph, node)                        \
+	for (count = 0, off = graph->nodes_start,                              \
+	     node = RTE_PTR_ADD(graph, off);                                   \
+	     count < graph->nb_nodes;                                          \
+	     off = node->next, node = RTE_PTR_ADD(graph, off), count++)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node object with in graph from id.
+ *
+ * @param graph_id
+ *   Graph id to get node pointer from.
+ * @param node_id
+ *   Node id to get node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get(rte_graph_t graph_id, uint32_t node_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node pointer with in graph from name.
+ *
+ * @param graph
+ *   Graph name to get node pointer from.
+ * @param name
+ *   Node name to get the node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get_by_name(const char *graph,
+					    const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create graph stats cluster to aggregate runtime node stats.
+ *
+ * @param prm
+ *   Parameters including file pointer to dump stats,
+ *   Graph pattern to create cluster and callback function.
+ *
+ * @return
+ *   Valid pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_graph_cluster_stats *rte_graph_cluster_stats_create(
+			const struct rte_graph_cluster_stats_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy cluster stats.
+ *
+ * @param stat
+ *    Valid cluster pointer to destroy.
+ *
+ */
+__rte_experimental
+void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get stats to application.
+ *
+ * @param[out] stat
+ *   Cluster status.
+ * @param skip_cb
+ *   true to skip callback function invocation.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat,
+				 bool skip_cb);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Reset cluster stats to zero.
+ *
+ * @param stat
+ *   Valid cluster stats pointer.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register new packet processing node. Nodes can be registered
+ * dynamically via this call or statically via the RTE_NODE_REGISTER
+ * macro.
+ *
+ * @param node
+ *   Valid node pointer with name, process function and next_nodes.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ *
+ * @see RTE_NODE_REGISTER()
+ */
+__rte_experimental
+rte_node_t __rte_node_register(const struct rte_node_register *node);
+
+/**
+ * Register a static node.
+ *
+ * The static node is registered through the constructor scheme, thereby, it can
+ * be used in a multi-process scenario.
+ *
+ * @param node
+ *   Valid node pointer with name, process function, and next_nodes.
+ */
+#define RTE_NODE_REGISTER(node)                                                \
+	RTE_INIT(rte_node_register_##node)                                     \
+	{                                                                      \
+		node.parent_id = RTE_NODE_ID_INVALID;                          \
+		node.id = __rte_node_register(&node);                          \
+	}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Clone a node from static node(node created from RTE_NODE_REGISTER).
+ *
+ * @param id
+ *   Static node id to clone from.
+ * @param name
+ *   Name of the new node. The library prepends the parent node name to the
+ * user-specified the name. The final node name will be,
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_clone(rte_node_t id, const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node id from node name.
+ *
+ * @param name
+ *   Valid node name. In the case of the cloned node, the name will be
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node name from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid node name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_node_id_to_name(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the number of edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_count(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Update the edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ * @param from
+ *   Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
+ * in that case, it will be added to the end of the list.
+ * @param next_nodes
+ *   Name of the edges to update.
+ * @param nb_edges
+ *   Number of edges to update.
+ *
+ * @return
+ *   Valid edge count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
+				const char **next_nodes, uint16_t nb_edges);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Shrink the edges to a given size.
+ *
+ * @param id
+ *   Valid node id.
+ * @param size
+ *   New size to shrink the edges.
+ *
+ * @return
+ *   New size on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the edge names from a given node.
+ *
+ * @param id
+ *   Valid node id.
+ * @param[out] next_nodes
+ *   Buffer to copy the edge names. The NULL value is allowed in that case,
+ * the function returns the size of the array that needs to be allocated.
+ *
+ * @return
+ *   When next_nodes == NULL, it returns the size of the array else
+ *  number of item copied.
+ */
+__rte_experimental
+rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum nodes created so far.
+ *
+ * @return
+ *   Maximum nodes count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ * @param id
+ *   Node id to get the info.
+ */
+__rte_experimental
+void rte_node_dump(FILE *f, rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ */
+__rte_experimental
+void rte_node_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of node id.
+ *
+ * @param id
+ *   Node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_node_is_invalid(rte_node_t id)
+{
+	return (id == RTE_NODE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of edge id.
+ *
+ * @param id
+ *   Edge node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_edge_is_invalid(rte_edge_t id)
+{
+	return (id == RTE_EDGE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of graph id.
+ *
+ * @param id
+ *   Graph id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_is_invalid(rte_graph_t id)
+{
+	return (id == RTE_GRAPH_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test stats feature support.
+ *
+ * @return
+ *   1 if stats enabled, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_has_stats_feature(void)
+{
+#ifdef RTE_LIBRTE_GRAPH_STATS
+	return RTE_LIBRTE_GRAPH_STATS;
+#else
+	return 0;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_H_ */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
new file mode 100644
index 000000000..55ef35df5
--- /dev/null
+++ b/lib/librte_graph/rte_graph_version.map
@@ -0,0 +1,3 @@
+EXPERIMENTAL {
+
+};
diff --git a/lib/meson.build b/lib/meson.build
index 9c3cc55d5..c43d86bb9 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index d295ca0a5..b1195f09a 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -98,6 +98,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
+_LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 02/26] graph: implement node registration
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 01/26] graph: define the public API for graph support jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 03/26] graph: implement node operations jerinj
                   ` (24 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding rte_node_register() API implementation includes allocating
memory for node object, check for duplicate node name and
add the allocated node to STAILQ node_list for future use.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph.c               |  18 +++-
 lib/librte_graph/graph_private.h       |  74 ++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/node.c                | 115 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   4 +
 6 files changed, 212 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/node.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 26fe514f3..933d0ee49 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -14,6 +14,7 @@ LDLIBS += -lrte_eal
 EXPORT_MAP := rte_graph_version.map
 
 # all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a55bf443a..a9c124896 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,4 +2,20 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
-#include "rte_graph.h"
+#include <rte_spinlock.h>
+
+#include "graph_private.h"
+
+static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+
+void
+graph_spinlock_lock(void)
+{
+	rte_spinlock_lock(&graph_lock);
+}
+
+void
+graph_spinlock_unlock(void)
+{
+	rte_spinlock_unlock(&graph_lock);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
new file mode 100644
index 000000000..ba56927fa
--- /dev/null
+++ b/lib/librte_graph/graph_private.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_PRIVATE_H_
+#define _RTE_GRAPH_PRIVATE_H_
+
+#include <inttypes.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_eal.h>
+#include <rte_graph.h>
+
+/**
+ * @internal
+ *
+ * Structure that holds node internal data.
+ */
+struct node {
+	STAILQ_ENTRY(node) next;      /**< Next node in the list. */
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+	rte_node_process_t process;   /**< Node process function. */
+	rte_node_init_t init;         /**< Node init function. */
+	rte_node_fini_t fini;	      /**< Node fini function. */
+	rte_node_t id;		      /**< Allocated identifier for the node. */
+	rte_node_t parent_id;	      /**< Parent node identifier. */
+	rte_edge_t nb_edges;	      /**< Number of edges from this node. */
+	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
+};
+
+/* Node functions */
+STAILQ_HEAD(node_head, node);
+
+/**
+ * @internal
+ *
+ * Get the head of the node list.
+ *
+ * @return
+ *   Pointer to the node head.
+ */
+struct node_head *node_list_head_get(void);
+
+/**
+ * @internal
+ *
+ * Get node pointer from node name.
+ *
+ * @param name
+ *   Pointer to character string containing the node name.
+ *
+ * @return
+ *   Pointer to the node.
+ */
+struct node *node_from_name(const char *name);
+
+/* Lock functions */
+/**
+ * @internal
+ *
+ * Take a lock on the graph internal spin lock.
+ */
+void graph_spinlock_lock(void);
+
+/**
+ * @internal
+ *
+ * Release a lock on the graph internal spin lock.
+ */
+void graph_spinlock_unlock(void);
+
+#endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 455cf2ba5..5754ac23b 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('graph.c')
+sources = files('node.c', 'graph.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
new file mode 100644
index 000000000..7999ca6ed
--- /dev/null
+++ b/lib/librte_graph/node.c
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_string_fns.h>
+
+#include "graph_private.h"
+
+static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
+static rte_node_t node_id;
+
+#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)
+
+/* Private functions */
+struct node_head *
+node_list_head_get(void)
+{
+	return &node_list;
+}
+
+struct node *
+node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static bool
+node_has_duplicate_entry(const char *name)
+{
+	struct node *node;
+
+	/* Is duplicate name registered */
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0) {
+			rte_errno = EEXIST;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Public functions */
+rte_node_t
+__rte_node_register(const struct rte_node_register *reg)
+{
+	struct node *node;
+	rte_edge_t i;
+	size_t sz;
+
+	graph_spinlock_lock();
+
+	/* Check sanity */
+	if (reg == NULL || reg->process == NULL) {
+		rte_errno = EINVAL;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(reg->name))
+		goto fail;
+
+	sz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);
+	node = calloc(1, sz);
+	if (node == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Initialize the node */
+	if (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {
+		rte_errno = E2BIG;
+		goto free;
+	}
+	node->flags = reg->flags;
+	node->process = reg->process;
+	node->init = reg->init;
+	node->fini = reg->fini;
+	node->nb_edges = reg->nb_edges;
+	node->parent_id = reg->parent_id;
+	for (i = 0; i < reg->nb_edges; i++) {
+		if (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto free;
+		}
+	}
+
+	node->id = node_id++;
+
+	/* Add the node at tail */
+	STAILQ_INSERT_TAIL(&node_list, node, next);
+	graph_spinlock_unlock();
+
+	return node->id;
+free:
+	free(node);
+fail:
+	graph_spinlock_unlock();
+	return RTE_NODE_ID_INVALID;
+}
+
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 55ef35df5..0884c09f1 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -1,3 +1,7 @@
 EXPERIMENTAL {
+	global:
 
+	__rte_node_register;
+
+	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 03/26] graph: implement node operations
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 01/26] graph: define the public API for graph support jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 02/26] graph: implement node registration jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 04/26] graph: implement node debug routines jerinj
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding node-specific API implementation like cloning node, updating
edges for the node, shrinking edges of a node, retrieving edges of a
node.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph_private.h       |  10 +
 lib/librte_graph/node.c                | 269 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  10 +
 3 files changed, 289 insertions(+)

diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index ba56927fa..28405ddb7 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -12,6 +12,16 @@
 #include <rte_eal.h>
 #include <rte_graph.h>
 
+
+#define ID_CHECK(id, id_max)                                                   \
+	do {                                                                   \
+		if ((id) >= (id_max)) {                                        \
+			rte_errno = EINVAL;                                    \
+			goto fail;                                             \
+		}                                                              \
+	} while (0)
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 7999ca6ed..8de857889 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -113,3 +113,272 @@ __rte_node_register(const struct rte_node_register *reg)
 	return RTE_NODE_ID_INVALID;
 }
 
+static int
+clone_name(struct rte_node_register *reg, struct node *node, const char *name)
+{
+	ssize_t sz, rc;
+
+#define SZ RTE_NODE_NAMESIZE
+	rc = rte_strscpy(reg->name, node->name, SZ);
+	if (rc < 0)
+		goto fail;
+	sz = rc;
+	rc = rte_strscpy(reg->name + sz, "-", RTE_MAX((int16_t)(SZ - sz), 0));
+	if (rc < 0)
+		goto fail;
+	sz += rc;
+	sz = rte_strscpy(reg->name + sz, name, RTE_MAX((int16_t)(SZ - sz), 0));
+	if (sz < 0)
+		goto fail;
+
+	return 0;
+fail:
+	rte_errno = E2BIG;
+	return -rte_errno;
+}
+
+static rte_node_t
+node_clone(struct node *node, const char *name)
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct rte_node_register *reg;
+	rte_edge_t i;
+
+	/* Don't allow to clone a node from a cloned node */
+	if (node->parent_id != RTE_NODE_ID_INVALID) {
+		rte_errno = EEXIST;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(name))
+		goto fail;
+
+	reg = calloc(1, sizeof(*reg) + (sizeof(char *) * node->nb_edges));
+	if (reg == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Clone the source node */
+	reg->flags = node->flags;
+	reg->process = node->process;
+	reg->init = node->init;
+	reg->fini = node->fini;
+	reg->nb_edges = node->nb_edges;
+	reg->parent_id = node->id;
+
+	for (i = 0; i < node->nb_edges; i++)
+		reg->next_nodes[i] = node->next_nodes[i];
+
+	/* Naming ceremony of the new node. name is node->name + "-" + name */
+	if (clone_name(reg, node, name))
+		goto free;
+
+	rc = __rte_node_register(reg);
+free:
+	free(reg);
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_clone(rte_node_t id, const char *name)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node_clone(node, name);
+
+fail:
+	return RTE_NODE_ID_INVALID;
+}
+
+rte_node_t
+rte_node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node->id;
+
+	return RTE_NODE_ID_INVALID;
+}
+
+char *
+rte_node_id_to_name(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->name;
+
+fail:
+	return NULL;
+}
+
+rte_edge_t
+rte_node_edge_count(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->nb_edges;
+fail:
+	return RTE_EDGE_ID_INVALID;
+}
+
+static rte_edge_t
+edge_update(struct node *node, struct node *prev, rte_edge_t from,
+	    const char **next_nodes, rte_edge_t nb_edges)
+{
+	rte_edge_t i, max_edges, count = 0;
+	struct node *new_node;
+	bool need_realloc;
+	size_t sz;
+
+	if (from == RTE_EDGE_ID_INVALID)
+		from = node->nb_edges;
+
+	/* Don't create hole in next_nodes[] list */
+	if (from > node->nb_edges) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Remove me from list */
+	STAILQ_REMOVE(&node_list, node, node, next);
+
+	/* Allocate the storage space for new node if required */
+	max_edges = from + nb_edges;
+	need_realloc = max_edges > node->nb_edges;
+	if (need_realloc) {
+		sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
+		new_node = realloc(node, sz);
+		if (new_node == NULL) {
+			rte_errno = ENOMEM;
+			goto restore;
+		} else {
+			node = new_node;
+		}
+	}
+
+	/* Update the new nodes name */
+	for (i = from; i < max_edges; i++, count++) {
+		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto restore;
+		}
+	}
+restore:
+	/* Update the linked list to point new node address in prev node */
+	if (prev)
+		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
+	else
+		STAILQ_INSERT_HEAD(&node_list, node, next);
+
+	if (need_realloc)
+		node->nb_edges += count;
+
+fail:
+	return count;
+}
+
+rte_edge_t
+rte_node_edge_shrink(rte_node_t id, rte_edge_t size)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (node->nb_edges < size) {
+				rte_errno = E2BIG;
+				goto fail;
+			}
+			node->nb_edges = size;
+			rc = size;
+			break;
+		}
+	}
+
+fail:
+	graph_spinlock_unlock();
+	return rc;
+}
+
+rte_edge_t
+rte_node_edge_update(rte_node_t id, rte_edge_t from, const char **next_nodes,
+		     uint16_t nb_edges)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *n, *prev;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	prev = NULL;
+	STAILQ_FOREACH(n, &node_list, next) {
+		if (n->id == id) {
+			rc = edge_update(n, prev, from, next_nodes, nb_edges);
+			break;
+		}
+		prev = n;
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+static rte_node_t
+node_copy_edges(struct node *node, char *next_nodes[])
+{
+	rte_edge_t i;
+
+	for (i = 0; i < node->nb_edges; i++)
+		next_nodes[i] = node->next_nodes[i];
+
+	return i;
+}
+
+rte_node_t
+rte_node_edge_get(rte_node_t id, char *next_nodes[])
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (next_nodes == NULL)
+				rc = sizeof(char *) * node->nb_edges;
+			else
+				rc = node_copy_edges(node, next_nodes);
+			break;
+		}
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_max_count(void)
+{
+	return node_id;
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 0884c09f1..412386356 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,5 +3,15 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 
+	rte_node_clone;
+	rte_node_edge_count;
+	rte_node_edge_get;
+	rte_node_edge_shrink;
+	rte_node_edge_update;
+	rte_node_from_name;
+	rte_node_id_to_name;
+	rte_node_list_dump;
+	rte_node_max_count;
+
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 04/26] graph: implement node debug routines
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (2 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 03/26] graph: implement node operations jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 05/26] graph: implement internal graph operation helpers jerinj
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding node debug API implementation support to dump
single or all the node objects to the given file.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/Makefile              |  1 +
 lib/librte_graph/graph_debug.c         | 25 ++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 12 ++++++++++
 lib/librte_graph/meson.build           |  2 +-
 lib/librte_graph/node.c                | 32 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 6 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_debug.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 933d0ee49..2a6d86933 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
new file mode 100644
index 000000000..75238e7ca
--- /dev/null
+++ b/lib/librte_graph/graph_debug.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_debug.h>
+
+#include "graph_private.h"
+
+void
+node_dump(FILE *f, struct node *n)
+{
+	rte_edge_t i;
+
+	fprintf(f, "node <%s>\n", n->name);
+	fprintf(f, "  id=%" PRIu32 "\n", n->id);
+	fprintf(f, "  flags=0x%" PRIx64 "\n", n->flags);
+	fprintf(f, "  addr=%p\n", n);
+	fprintf(f, "  process=%p\n", n->process);
+	fprintf(f, "  nb_edges=%d\n", n->nb_edges);
+
+	for (i = 0; i < n->nb_edges; i++)
+		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
+}
+
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 28405ddb7..a8efee7c8 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -81,4 +81,16 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/**
+ * @internal
+ *
+ * Dump internal node object data.
+ *
+ * @param f
+ *   FILE pointer to dump the info.
+ * @param g
+ *   Pointer to the internal node object.
+ */
+void node_dump(FILE *f, struct node *n);
+
 #endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 5754ac23b..01512182f 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c')
+sources = files('node.c', 'graph.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 8de857889..2f9c2ea4c 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
 	return rc;
 }
 
+static void
+node_scan_dump(FILE *f, rte_node_t id, bool all)
+{
+	struct node *node;
+
+	RTE_ASSERT(f != NULL);
+	NODE_ID_CHECK(id);
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (all == true) {
+			node_dump(f, node);
+		} else if (node->id == id) {
+			node_dump(f, node);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_node_dump(FILE *f, rte_node_t id)
+{
+	node_scan_dump(f, id, false);
+}
+
+void
+rte_node_list_dump(FILE *f)
+{
+	node_scan_dump(f, 0, true);
+}
+
 rte_node_t
 rte_node_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 412386356..f2c2139c5 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	__rte_node_register;
 
 	rte_node_clone;
+	rte_node_dump;
 	rte_node_edge_count;
 	rte_node_edge_get;
 	rte_node_edge_shrink;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 05/26] graph: implement internal graph operation helpers
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (3 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 04/26] graph: implement node debug routines jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 06/26] graph: populate fastpath memory for graph reel jerinj
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding internal graph API helpers support to check whether a graph has
isolated nodes and any node have a loop to itself and BFS
algorithm implementation etc.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
---
 lib/librte_graph/Makefile        |   1 +
 lib/librte_graph/graph.c         |   2 +-
 lib/librte_graph/graph_ops.c     | 169 ++++++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h | 173 +++++++++++++++++++++++++++++++
 lib/librte_graph/meson.build     |   2 +-
 5 files changed, 345 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_ops.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 2a6d86933..39ecb2652 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a9c124896..4c3f2fe7b 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -7,7 +7,7 @@
 #include "graph_private.h"
 
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
-
+int rte_graph_logtype;
 void
 graph_spinlock_lock(void)
 {
diff --git a/lib/librte_graph/graph_ops.c b/lib/librte_graph/graph_ops.c
new file mode 100644
index 000000000..335595311
--- /dev/null
+++ b/lib/librte_graph/graph_ops.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+
+#include "graph_private.h"
+
+/* Check whether a node has next_node to itself */
+static inline int
+node_has_loop_edge(struct node *node)
+{
+	rte_edge_t i;
+	char *name;
+	int rc = 0;
+
+	for (i = 0; i < node->nb_edges; i++) {
+		if (strncmp(node->name, node->next_nodes[i],
+			    RTE_NODE_NAMESIZE) == 0) {
+			name = node->name;
+			rc = 1;
+			SET_ERR_JMP(EINVAL, fail, "Node %s has loop to self",
+				    name);
+		}
+	}
+fail:
+	return rc;
+}
+
+int
+graph_node_has_loop_edge(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (node_has_loop_edge(graph_node->node))
+			return 1;
+
+	return 0;
+}
+
+rte_node_t
+graph_src_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t rc = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F)
+			rc++;
+
+	if (rc == 0)
+		SET_ERR_JMP(EINVAL, fail, "Graph needs at least a source node");
+fail:
+	return rc;
+}
+
+/* Check whether a node has next_node to a source node */
+int
+graph_node_has_edge_to_src_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *node;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			node = graph_node->adjacency_list[i]->node;
+			if (node->flags & RTE_NODE_SOURCE_F)
+				SET_ERR_JMP(
+					EEXIST, fail,
+					"Node %s points to the source node %s",
+					graph_node->node->name, node->name);
+		}
+	}
+
+	return 0;
+fail:
+	return 1;
+}
+
+rte_node_t
+graph_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t count = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		count++;
+
+	return count;
+}
+
+void
+graph_mark_nodes_as_not_visited(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		graph_node->visited = false;
+}
+
+int
+graph_bfs(struct graph *graph, struct graph_node *start)
+{
+	struct graph_node **queue, *v, *tmp;
+	uint16_t head = 0, tail = 0;
+	rte_edge_t i;
+	size_t sz;
+
+	sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
+	queue = malloc(sz);
+	if (queue == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
+			    sz);
+
+	/* BFS algorithm */
+	queue[tail++] = start;
+	start->visited = true;
+	while (head != tail) {
+		v = queue[head++];
+		for (i = 0; i < v->node->nb_edges; i++) {
+			tmp = v->adjacency_list[i];
+			if (tmp->visited == false) {
+				queue[tail++] = tmp;
+				tmp->visited = true;
+			}
+		}
+	}
+
+	free(queue);
+	return 0;
+
+fail:
+	return -rte_errno;
+}
+
+/* Check whether a node has connected path or parent node */
+int
+graph_has_isolated_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	graph_mark_nodes_as_not_visited(graph);
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			if (graph_node->node->nb_edges == 0)
+				SET_ERR_JMP(EINVAL, fail,
+					    "%s node needs minimum one edge",
+					    graph_node->node->name);
+			if (graph_bfs(graph, graph_node))
+				goto fail;
+		}
+	}
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->visited == false)
+			SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
+				    graph_node->node->name);
+
+	return 0;
+fail:
+	return 1;
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index a8efee7c8..7bf491d3d 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -12,6 +12,16 @@
 #include <rte_eal.h>
 #include <rte_graph.h>
 
+extern int rte_graph_logtype;
+
+#define GRAPH_LOG(level, ...)                                                  \
+	rte_log(RTE_LOG_##level, rte_graph_logtype,                            \
+		RTE_FMT("GRAPH: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",    \
+			__func__, __LINE__, RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define graph_err(...) GRAPH_LOG(ERR, __VA_ARGS__)
+#define graph_info(...) GRAPH_LOG(INFO, __VA_ARGS__)
+#define graph_dbg(...) GRAPH_LOG(DEBUG, __VA_ARGS__)
 
 #define ID_CHECK(id, id_max)                                                   \
 	do {                                                                   \
@@ -21,6 +31,12 @@
 		}                                                              \
 	} while (0)
 
+#define SET_ERR_JMP(err, where, fmt, ...)                                      \
+	do {                                                                   \
+		graph_err(fmt, ##__VA_ARGS__);                                 \
+		rte_errno = err;                                               \
+		goto where;                                                    \
+	} while (0)
 
 /**
  * @internal
@@ -40,6 +56,52 @@ struct node {
 	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
 };
 
+/**
+ * @internal
+ *
+ * Structure that holds the graph node data.
+ */
+struct graph_node {
+	STAILQ_ENTRY(graph_node) next; /**< Next graph node in the list. */
+	struct node *node; /**< Pointer to internal node. */
+	bool visited;      /**< Flag used in BFS to mark node visited. */
+	struct graph_node *adjacency_list[]; /**< Adjacency list of the node. */
+};
+
+/**
+ * @internal
+ *
+ * Structure that holds graph internal data.
+ */
+struct graph {
+	STAILQ_ENTRY(graph) next;
+	/**< List of graphs. */
+	char name[RTE_GRAPH_NAMESIZE];
+	/**< Name of the graph. */
+	const struct rte_memzone *mz;
+	/**< Memzone to store graph data. */
+	rte_graph_off_t nodes_start;
+	/**< Node memory start offset in graph reel. */
+	rte_node_t src_node_count;
+	/**< Number of source nodes in a graph. */
+	struct rte_graph *graph;
+	/**< Pointer to graph data. */
+	rte_node_t node_count;
+	/**< Total number of nodes. */
+	uint32_t cir_start;
+	/**< Circular buffer start offset in graph reel. */
+	uint32_t cir_mask;
+	/**< Circular buffer mask for wrap around. */
+	rte_graph_t id;
+	/**< Graph identifier. */
+	size_t mem_sz;
+	/**< Memory size of the graph. */
+	int socket;
+	/**< Socket identifier where memory is allocated. */
+	STAILQ_HEAD(gnode_list, graph_node) node_list;
+	/**< Nodes in a graph. */
+};
+
 /* Node functions */
 STAILQ_HEAD(node_head, node);
 
@@ -66,6 +128,19 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);
 
+/* Graph list functions */
+STAILQ_HEAD(graph_head, graph);
+
+/**
+ * @internal
+ *
+ * Get the head of the graph list.
+ *
+ * @return
+ *   Pointer to the graph head.
+ */
+struct graph_head *graph_list_head_get(void);
+
 /* Lock functions */
 /**
  * @internal
@@ -81,6 +156,104 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/* Graph operations */
+/**
+ * @internal
+ *
+ * Run a BFS(Breadth First Search) on the graph marking all
+ * the graph nodes as visited.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ * @param start
+ *   Pointer to the starting graph node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough memory for BFS.
+ */
+int graph_bfs(struct graph *graph, struct graph_node *start);
+
+/**
+ * @internal
+ *
+ * Check if there is an isolated node in the given graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: No isolated node found.
+ *   - 1: Isolated node found.
+ */
+int graph_has_isolated_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Check whether a node in the graph has next_node to a source node.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to source node.
+ *   - 1: Node doesn't have an edge to source node.
+ */
+int graph_node_has_edge_to_src_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Checks whether node in the graph has a edge to itself i.e. forms a
+ * loop.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to itself.
+ *   - 1: Node doesn't have an edge to itself.
+ */
+int graph_node_has_loop_edge(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of source nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of source nodes.
+ */
+rte_node_t graph_src_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of total number of nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of nodes.
+ */
+rte_node_t graph_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Clear the visited flag of all the nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ */
+void graph_mark_nodes_as_not_visited(struct graph *graph);
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 01512182f..16e0625c1 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_debug.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 06/26] graph: populate fastpath memory for graph reel
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (4 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 05/26] graph: implement internal graph operation helpers jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 07/26] graph: implement create and destroy APIs jerinj
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding support to create and populate the memory for graph reel.
This includes reserving the memory in the memzone, populating the nodes,
Allocating memory for node-specific streams to hold objects.

Once it is populated the reel memory contains the following sections.

+---------------------+
|   Graph Header      |
+---------------------+
|   Fence             |
+---------------------+
|   Circular buffer   |
+---------------------+
|   Fence             |
+---------------------+
|   Node Object 0     |
+------------------- -+
|   Node Object 1     |
+------------------- -+
|   Node Object 2     |
+------------------- -+
|   Node Object n     |
+------------------- -+

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/Makefile              |   2 +
 lib/librte_graph/graph.c               |  16 ++
 lib/librte_graph/graph_populate.c      | 234 +++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       |  64 +++++++
 lib/librte_graph/meson.build           |   4 +-
 lib/librte_graph/node.c                |   5 +
 lib/librte_graph/rte_graph_version.map |   1 +
 lib/librte_graph/rte_graph_worker.h    | 107 +++++++++++
 8 files changed, 431 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/rte_graph_worker.h

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 39ecb2652..7bfd7d51f 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,8 +18,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph_worker.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 4c3f2fe7b..e1930b7d2 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,6 +2,7 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <rte_malloc.h>
 #include <rte_spinlock.h>
 
 #include "graph_private.h"
@@ -19,3 +20,18 @@ graph_spinlock_unlock(void)
 {
 	rte_spinlock_unlock(&graph_lock);
 }
+
+void __rte_noinline
+__rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
diff --git a/lib/librte_graph/graph_populate.c b/lib/librte_graph/graph_populate.c
new file mode 100644
index 000000000..093512efa
--- /dev/null
+++ b/lib/librte_graph/graph_populate.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "graph_private.h"
+
+static size_t
+graph_fp_mem_calc_size(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t val;
+	size_t sz;
+
+	/* Graph header */
+	sz = sizeof(struct rte_graph);
+	/* Source nodes list */
+	sz += sizeof(rte_graph_off_t) * graph->src_node_count;
+	/* Circular buffer for pending streams of size number of nodes */
+	val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
+	sz = RTE_ALIGN(sz, val);
+	graph->cir_start = sz;
+	graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
+	sz += val;
+	/* Fence */
+	sz += sizeof(RTE_GRAPH_FENCE);
+	sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+	graph->nodes_start = sz;
+	/* For 0..N node objects with fence */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+		sz += sizeof(struct rte_node);
+		/* Pointer to next nodes(edges) */
+		sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
+	}
+
+	graph->mem_sz = sz;
+	return sz;
+}
+
+static void
+graph_header_popluate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+
+	graph->tail = 0;
+	graph->head = (int32_t)-_graph->src_node_count;
+	graph->cir_mask = _graph->cir_mask;
+	graph->nb_nodes = _graph->node_count;
+	graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
+	graph->nodes_start = _graph->nodes_start;
+	graph->socket = _graph->socket;
+	graph->id = _graph->id;
+	memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);
+	graph->fence = RTE_GRAPH_FENCE;
+}
+
+static void
+graph_nodes_populate(struct graph *_graph)
+{
+	rte_graph_off_t off = _graph->nodes_start;
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	rte_edge_t count, nb_edges;
+	const char *parent;
+	rte_node_t pid;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		struct rte_node *node = RTE_PTR_ADD(graph, off);
+		memset(node, 0, sizeof(*node));
+		node->fence = RTE_GRAPH_FENCE;
+		node->off = off;
+		node->process = graph_node->node->process;
+		memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
+		pid = graph_node->node->parent_id;
+		if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
+			parent = rte_node_id_to_name(pid);
+			memcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);
+		}
+		node->id = graph_node->node->id;
+		node->parent_id = pid;
+		nb_edges = graph_node->node->nb_edges;
+		node->nb_edges = nb_edges;
+		off += sizeof(struct rte_node);
+		/* Copy the name in first pass to replace with rte_node* later*/
+		for (count = 0; count < nb_edges; count++)
+			node->nodes[count] = (struct rte_node *)&graph_node
+						     ->adjacency_list[count]
+						     ->node->name[0];
+
+		off += sizeof(struct rte_node *) * nb_edges;
+		off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
+		node->next = off;
+		__rte_node_stream_alloc(graph, node);
+	}
+}
+
+struct rte_node *
+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (unlikely(node->id == id))
+			return node;
+
+	return NULL;
+}
+
+struct rte_node *
+graph_node_name_to_ptr(const struct rte_graph *graph, const char *name)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (strncmp(name, node->name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static int
+graph_node_nexts_populate(struct graph *_graph)
+{
+	rte_node_t count, val;
+	rte_graph_off_t off;
+	struct rte_node *node;
+	const struct rte_graph *graph = _graph->graph;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		for (val = 0; val < node->nb_edges; val++) {
+			name = (const char *)node->nodes[val];
+			node->nodes[val] = graph_node_name_to_ptr(graph, name);
+			if (node->nodes[val] == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_src_nodes_populate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	struct rte_node *node;
+	int32_t head = -1;
+	const char *name;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			name = graph_node->node->name;
+			node = graph_node_name_to_ptr(graph, name);
+			if (node == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+
+			__rte_node_stream_alloc(graph, node);
+			graph->cir_start[head--] = node->off;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_fp_mem_populate(struct graph *graph)
+{
+	int rc;
+
+	graph_header_popluate(graph);
+	graph_nodes_populate(graph);
+	rc = graph_node_nexts_populate(graph);
+	rc |= graph_src_nodes_populate(graph);
+
+	return rc;
+}
+
+int
+graph_fp_mem_create(struct graph *graph)
+{
+	const struct rte_memzone *mz;
+	size_t sz;
+
+	sz = graph_fp_mem_calc_size(graph);
+	mz = rte_memzone_reserve(graph->name, sz, graph->socket, 0);
+	if (mz == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Memzone %s reserve failed",
+			    graph->name);
+
+	graph->graph = mz->addr;
+	graph->mz = mz;
+
+	return graph_fp_mem_populate(graph);
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_nodes_mem_destroy(struct rte_graph *graph)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	if (graph == NULL)
+		return;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		rte_free(node->objs);
+}
+
+int
+graph_fp_mem_destroy(struct graph *graph)
+{
+	graph_nodes_mem_destroy(graph->graph);
+	return rte_memzone_free(graph->mz);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7bf491d3d..051fad53a 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -11,6 +11,7 @@
 #include <rte_common.h>
 #include <rte_eal.h>
 #include <rte_graph.h>
+#include <rte_graph_worker.h>
 
 extern int rte_graph_logtype;
 
@@ -253,6 +254,69 @@ rte_node_t graph_nodes_count(struct graph *graph);
  */
 void graph_mark_nodes_as_not_visited(struct graph *graph);
 
+/* Fast path graph memory populate unctions */
+
+/**
+ * @internal
+ *
+ * Create fast-path memory for the graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough for graph and nodes.
+ *   - -EINVAL: Graph nodes not found.
+ */
+int graph_fp_mem_create(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Free fast-path memory used by graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Graph memzone related error.
+ */
+int graph_fp_mem_destroy(struct graph *graph);
+
+/* Lookup functions */
+/**
+ * @internal
+ *
+ * Get graph node object from node id.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param id
+ *   Node Identifier.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
+				      rte_node_t id);
+
+/**
+ * @internal
+ *
+ * Get graph node object from node name.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param node_name
+ *   Pointer to character string holding the node name.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
+					const char *node_name);
 
 /**
  * @internal
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 16e0625c1..fb203a5e2 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,8 +4,8 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
-headers = files('rte_graph.h')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
 deps += ['eal']
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 2f9c2ea4c..639269870 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -61,6 +61,11 @@ __rte_node_register(const struct rte_node_register *reg)
 	rte_edge_t i;
 	size_t sz;
 
+	/* Limit Node specific metadata to one cacheline on 64B CL machine */
+	RTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -
+			  offsetof(struct rte_node, ctx)) !=
+			 RTE_CACHE_LINE_MIN_SIZE);
+
 	graph_spinlock_lock();
 
 	/* Check sanity */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index f2c2139c5..a9fe1b610 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -2,6 +2,7 @@ EXPERIMENTAL {
 	global:
 
 	__rte_node_register;
+	__rte_node_stream_alloc;
 
 	rte_node_clone;
 	rte_node_dump;
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
new file mode 100644
index 000000000..a7c780d4d
--- /dev/null
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_WORKER_H_
+#define _RTE_GRAPH_WORKER_H_
+
+/**
+ * @file rte_graph_worker.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows a worker thread to walk over a graph and nodes to create,
+ * process, enqueue and move streams of objects to the next nodes.
+ */
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_graph.h>
+#include <rte_prefetch.h>
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ *
+ * Data structure to hold graph data.
+ */
+struct rte_graph {
+	uint32_t tail;		     /**< Tail of circular buffer. */
+	uint32_t head;		     /**< Head of circular buffer. */
+	uint32_t cir_mask;	     /**< Circular buffer wrap around mask. */
+	rte_node_t nb_nodes;	     /**< Number of nodes in the graph. */
+	rte_graph_off_t *cir_start;  /**< Pointer to circular buffer. */
+	rte_graph_off_t nodes_start; /**< Offset at which node memory starts. */
+	rte_graph_t id;	/**< Graph identifier. */
+	int socket;	/**< Socket ID where memory is allocated. */
+	char name[RTE_GRAPH_NAMESIZE];	/**< Name of the graph. */
+	uint64_t fence;			/**< Fence. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Data structure to hold node data.
+ */
+struct rte_node {
+	/* Slow path area  */
+	uint64_t fence;		/**< Fence. */
+	rte_graph_off_t next;	/**< Index to next node. */
+	rte_node_t id;		/**< Node identifier. */
+	rte_node_t parent_id;	/**< Parent Node identifier. */
+	rte_edge_t nb_edges;	/**< Number of edges from this node. */
+	uint32_t realloc_count;	/**< Number of times realloced. */
+
+	char parent[RTE_NODE_NAMESIZE];	/**< Parent node name. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+
+	/* Fast path area  */
+#define RTE_NODE_CTX_SZ 16
+	uint8_t ctx[RTE_NODE_CTX_SZ] __rte_cache_aligned; /**< Node Context. */
+	uint16_t size;		/**< Total number of objects available. */
+	uint16_t idx;		/**< Number of objects used. */
+	rte_graph_off_t off;	/**< Offset of node in the graph reel. */
+	uint64_t total_cycles;	/**< Cycles spent in this node. */
+	uint64_t total_calls;	/**< Calls done to this node. */
+	uint64_t total_objs;	/**< Objects processed by this node. */
+	RTE_STD_C11
+		union {
+			void **objs;	   /**< Array of object pointers. */
+			uint64_t objs_u64;
+		};
+	RTE_STD_C11
+		union {
+			rte_node_process_t process; /**< Process function. */
+			uint64_t process_u64;
+		};
+	struct rte_node *nodes[] __rte_cache_min_aligned; /**< Next nodes. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ */
+__rte_experimental
+void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_WORKER_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 07/26] graph: implement create and destroy APIs
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (5 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 06/26] graph: populate fastpath memory for graph reel jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 08/26] graph: implement graph operation APIs jerinj
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding graph specific API implementations like graph create
and graph destroy. This detect loops in the graph,
check for isolated nodes and operation to verify the validity of
graph.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 321 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   2 +
 2 files changed, 323 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e1930b7d2..d060ffe70 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,13 +2,34 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_errno.h>
+#include <rte_graph.h>
 #include <rte_malloc.h>
+#include <rte_memzone.h>
 #include <rte_spinlock.h>
+#include <rte_string_fns.h>
 
 #include "graph_private.h"
 
+static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+static rte_graph_t graph_id;
 int rte_graph_logtype;
+
+#define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id)
+
+/* Private functions */
+struct graph_head *
+graph_list_head_get(void)
+{
+	return &graph_list;
+}
+
 void
 graph_spinlock_lock(void)
 {
@@ -21,6 +42,306 @@ graph_spinlock_unlock(void)
 	rte_spinlock_unlock(&graph_lock);
 }
 
+static int
+graph_node_add(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+	size_t sz;
+
+	/* Skip the duplicate nodes */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (strncmp(node->name, graph_node->node->name,
+			    RTE_NODE_NAMESIZE) == 0)
+			return 0;
+
+	/* Allocate new graph node object */
+	sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);
+	graph_node = calloc(1, sz);
+
+	if (graph_node == NULL)
+		SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object",
+			    node->name);
+
+	/* Initialize the graph node */
+	graph_node->node = node;
+
+	/* Add to graph node list */
+	STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);
+	return 0;
+
+free:
+	free(graph_node);
+	return -rte_errno;
+}
+
+static struct graph_node *
+node_to_graph_node(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node == node)
+			return graph_node;
+
+	SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name);
+fail:
+	return NULL;
+}
+
+static int
+graph_node_edges_add(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			if (graph_node_add(graph, adjacency))
+				goto fail;
+		}
+	}
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_adjacency_list_update(struct graph *graph)
+{
+	struct graph_node *graph_node, *tmp;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			tmp = node_to_graph_node(graph, adjacency);
+			if (tmp == NULL)
+				goto fail;
+			graph_node->adjacency_list[i] = tmp;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+expand_pattern_to_node(struct graph *graph, const char *pattern)
+{
+	struct node_head *node_head = node_list_head_get();
+	bool found = false;
+	struct node *node;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(node, node_head, next) {
+		if (fnmatch(pattern, node->name, 0) == 0) {
+			if (graph_node_add(graph, node))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_cleanup(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	while (!STAILQ_EMPTY(&graph->node_list)) {
+		graph_node = STAILQ_FIRST(&graph->node_list);
+		STAILQ_REMOVE_HEAD(&graph->node_list, next);
+		free(graph_node);
+	}
+}
+
+static int
+graph_node_init(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	const char *name;
+	int rc;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->init) {
+			name = graph_node->node->name;
+			rc = graph_node->node->init(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph, name));
+			if (rc)
+				SET_ERR_JMP(rc, err, "Node %s init() failed",
+					    name);
+		}
+	}
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+graph_node_fini(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->fini)
+			graph_node->node->fini(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph,
+						       graph_node->node->name));
+}
+
+rte_graph_t
+rte_graph_create(const char *name, struct rte_graph_param *prm)
+{
+	struct graph *graph;
+	const char *pattern;
+	uint16_t i;
+
+	graph_spinlock_lock();
+
+	/* Check arguments sanity */
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
+
+	if (name == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
+
+	/* Check for existence of duplicate graph */
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
+			SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
+				    name);
+
+	/* Create graph object */
+	graph = calloc(1, sizeof(*graph));
+	if (graph == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
+
+	/* Initialize the graph object */
+	STAILQ_INIT(&graph->node_list);
+	if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
+		SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
+
+	/* Expand node pattern and add the nodes to the graph */
+	for (i = 0; i < prm->nb_node_patterns; i++) {
+		pattern = prm->node_patterns[i];
+		if (expand_pattern_to_node(graph, pattern))
+			goto graph_cleanup;
+	}
+
+	/* Go over all the nodes edges and add them to the graph */
+	if (graph_node_edges_add(graph))
+		goto graph_cleanup;
+
+	/* Update adjacency list of all nodes in the graph */
+	if (graph_adjacency_list_update(graph))
+		goto graph_cleanup;
+
+	/* Make sure at least a source node present in the graph */
+	if (!graph_src_nodes_count(graph))
+		goto graph_cleanup;
+
+	/* Make sure no node is pointing to source node */
+	if (graph_node_has_edge_to_src_node(graph))
+		goto graph_cleanup;
+
+	/* Don't allow node has loop to self */
+	if (graph_node_has_loop_edge(graph))
+		goto graph_cleanup;
+
+	/* Do BFS from src nodes on the graph to find isolated nodes */
+	if (graph_has_isolated_node(graph))
+		goto graph_cleanup;
+
+	/* Initialize graph object */
+	graph->socket = prm->socket_id;
+	graph->src_node_count = graph_src_nodes_count(graph);
+	graph->node_count = graph_nodes_count(graph);
+	graph->id = graph_id;
+
+	/* Allocate the Graph fast path memory and populate the data */
+	if (graph_fp_mem_create(graph))
+		goto graph_cleanup;
+
+	/* Call init() of the all the nodes in the graph */
+	if (graph_node_init(graph))
+		goto graph_mem_destroy;
+
+	/* All good, Lets add the graph to the list */
+	graph_id++;
+	STAILQ_INSERT_TAIL(&graph_list, graph, next);
+
+	graph_spinlock_unlock();
+	return graph->id;
+
+graph_mem_destroy:
+	graph_fp_mem_destroy(graph);
+graph_cleanup:
+	graph_cleanup(graph);
+free:
+	free(graph);
+fail:
+	graph_spinlock_unlock();
+	return RTE_GRAPH_ID_INVALID;
+}
+
+rte_graph_t
+rte_graph_destroy(const char *graph_name)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph, *tmp;
+	const char *name;
+
+	graph_spinlock_lock();
+
+	graph = STAILQ_FIRST(&graph_list);
+	while (graph != NULL) {
+		tmp = STAILQ_NEXT(graph, next);
+		name = graph->name;
+		if (strncmp(name, graph_name, RTE_GRAPH_NAMESIZE) == 0) {
+			/* Call fini() of the all the nodes in the graph */
+			graph_node_fini(graph);
+			/* Destroy graph fast path memory */
+			rc = graph_fp_mem_destroy(graph);
+			if (rc)
+				SET_ERR_JMP(rc, done, "MZ %s free failed",
+					    name);
+
+			graph_cleanup(graph);
+			STAILQ_REMOVE(&graph_list, graph, graph, next);
+			rc = graph->id;
+			free(graph);
+			graph_id--;
+			goto done;
+		}
+		graph = tmp;
+	}
+done:
+	graph_spinlock_unlock();
+	return rc;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index a9fe1b610..dcbd78c02 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,8 @@ EXPERIMENTAL {
 	__rte_node_register;
 	__rte_node_stream_alloc;
 
+	rte_graph_create;
+	rte_graph_destroy;
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 08/26] graph: implement graph operation APIs
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (6 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 07/26] graph: implement create and destroy APIs jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 09/26] graph: implement Graphviz export jerinj
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K, Anatoly Burakov
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding support for graph specific API implementation like
Graph lookup to get graph object, retrieving graph ID
From name and graph name from ID.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 131 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   8 ++
 2 files changed, 139 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index d060ffe70..72a82c2a8 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -211,6 +211,54 @@ graph_node_fini(struct graph *graph)
 						       graph_node->node->name));
 }
 
+static struct rte_graph *
+graph_mem_fixup_node_ctx(struct rte_graph *graph)
+{
+	struct rte_node *node;
+	struct node *node_db;
+	rte_graph_off_t off;
+	rte_node_t count;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
+			name = node->name;
+		else /* Cloned node */
+			name = node->parent;
+
+		node_db = node_from_name(name);
+		if (node_db == NULL)
+			SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
+		node->process = node_db->process;
+	}
+
+	return graph;
+fail:
+	return NULL;
+}
+
+static struct rte_graph *
+graph_mem_fixup_secondray(struct rte_graph *graph)
+{
+	if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
+		return graph;
+
+	return graph_mem_fixup_node_ctx(graph);
+}
+
+struct rte_graph *
+rte_graph_lookup(const char *name)
+{
+	const struct rte_memzone *mz;
+	struct rte_graph *rc = NULL;
+
+	mz = rte_memzone_lookup(name);
+	if (mz)
+		rc = mz->addr;
+
+	return graph_mem_fixup_secondray(rc);
+}
+
 rte_graph_t
 rte_graph_create(const char *name, struct rte_graph_param *prm)
 {
@@ -342,6 +390,76 @@ rte_graph_destroy(const char *graph_name)
 	return rc;
 }
 
+rte_graph_t
+rte_graph_from_name(const char *name)
+{
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)
+			return graph->id;
+
+	return RTE_GRAPH_ID_INVALID;
+}
+
+char *
+rte_graph_id_to_name(rte_graph_t id)
+{
+	struct graph *graph;
+
+	GRAPH_ID_CHECK(id);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == id)
+			return graph->name;
+
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get(rte_graph_t gid, uint32_t nid)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	GRAPH_ID_CHECK(gid);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == gid) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (node->id == nid)
+					return node;
+			}
+			break;
+		}
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get_by_name(const char *graph_name, const char *node_name)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (!strncmp(node->name, node_name,
+					     RTE_NODE_NAMESIZE))
+					return node;
+			}
+			break;
+		}
+
+	return NULL;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
@@ -356,3 +474,16 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->size = size;
 	node->realloc_count++;
 }
+
+rte_graph_t
+rte_graph_max_count(void)
+{
+	return graph_id;
+}
+
+RTE_INIT(rte_graph_init_log)
+{
+	rte_graph_logtype = rte_log_register("lib.graph");
+	if (rte_graph_logtype >= 0)
+		rte_log_set_level(rte_graph_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index dcbd78c02..5a2b13293 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,14 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_from_name;
+	rte_graph_id_to_name;
+	rte_graph_lookup;
+	rte_graph_list_dump;
+	rte_graph_max_count;
+	rte_graph_node_get;
+	rte_graph_node_get_by_name;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 09/26] graph: implement Graphviz export
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (7 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 08/26] graph: implement graph operation APIs jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 10/26] graph: implement debug routines jerinj
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding API implementation support exporting the graph object to file.
This will export the graph to a file in Graphviz format.
It can be viewed in many viewers such as
https://dreampuf.github.io/GraphvizOnline/

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 54 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 2 files changed, 55 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 72a82c2a8..ad58673be 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -475,6 +475,60 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+static int
+graph_to_dot(FILE *f, struct graph *graph)
+{
+	const char *src_edge_color = " [color=blue]\n";
+	const char *edge_color = "\n";
+	struct graph_node *graph_node;
+	char *node_name;
+	rte_edge_t i;
+	int rc;
+
+	rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name);
+	if (rc < 0)
+		goto end;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		node_name = graph_node->node->name;
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name,
+				     graph_node->adjacency_list[i]->node->name,
+				     graph_node->node->flags & RTE_NODE_SOURCE_F
+					     ? src_edge_color
+					     : edge_color);
+			if (rc < 0)
+				goto end;
+		}
+	}
+	rc = fprintf(f, "}\n");
+	if (rc < 0)
+		goto end;
+
+	return 0;
+end:
+	rte_errno = EBADF;
+	return -rte_errno;
+}
+
+rte_graph_t
+rte_graph_export(const char *name, FILE *f)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {
+			rc = graph_to_dot(f, graph);
+			goto end;
+		}
+	}
+	rte_errno = ENOENT;
+end:
+	return rc;
+}
+
+
 rte_graph_t
 rte_graph_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 5a2b13293..2797be044 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
 	rte_graph_lookup;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 10/26] graph: implement debug routines
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (8 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 09/26] graph: implement Graphviz export jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 11/26] graph: implement stats support jerinj
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph specific API to dump the
Graph information to a file. This API will dump detailed internal
info about node objects and graph objects.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 31 ++++++++++++++
 lib/librte_graph/graph_debug.c         | 59 ++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 13 ++++++
 lib/librte_graph/rte_graph_version.map |  2 +
 4 files changed, 105 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index ad58673be..cc1e523d9 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -528,6 +528,37 @@ rte_graph_export(const char *name, FILE *f)
 	return rc;
 }
 
+static void
+graph_scan_dump(FILE *f, rte_graph_t id, bool all)
+{
+	struct graph *graph;
+
+	RTE_VERIFY(f);
+	GRAPH_ID_CHECK(id);
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (all == true) {
+			graph_dump(f, graph);
+		} else if (graph->id == id) {
+			graph_dump(f, graph);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_graph_dump(FILE *f, rte_graph_t id)
+{
+	graph_scan_dump(f, id, false);
+}
+
+void
+rte_graph_list_dump(FILE *f)
+{
+	graph_scan_dump(f, 0, true);
+}
 
 rte_graph_t
 rte_graph_max_count(void)
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
index 75238e7ca..f8aea16ac 100644
--- a/lib/librte_graph/graph_debug.c
+++ b/lib/librte_graph/graph_debug.c
@@ -7,6 +7,26 @@
 
 #include "graph_private.h"
 
+void
+graph_dump(FILE *f, struct graph *g)
+{
+	struct graph_node *graph_node;
+	rte_edge_t i = 0;
+
+	fprintf(f, "graph <%s>\n", g->name);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  cir_start=%" PRIu32 "\n", g->cir_start);
+	fprintf(f, "  cir_mask=%" PRIu32 "\n", g->cir_mask);
+	fprintf(f, "  addr=%p\n", g);
+	fprintf(f, "  graph=%p\n", g->graph);
+	fprintf(f, "  mem_sz=%zu\n", g->mem_sz);
+	fprintf(f, "  node_count=%" PRIu32 "\n", g->node_count);
+	fprintf(f, "  src_node_count=%" PRIu32 "\n", g->src_node_count);
+
+	STAILQ_FOREACH(graph_node, &g->node_list, next)
+		fprintf(f, "     node[%d] <%s>\n", i++, graph_node->node->name);
+}
+
 void
 node_dump(FILE *f, struct node *n)
 {
@@ -23,3 +43,42 @@ node_dump(FILE *f, struct node *n)
 		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
 }
 
+void
+rte_graph_obj_dump(FILE *f, struct rte_graph *g, bool all)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *n;
+	rte_edge_t i;
+
+	fprintf(f, "graph <%s> @ %p\n", g->name, g);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  head=%" PRId32 "\n", (int32_t)g->head);
+	fprintf(f, "  tail=%" PRId32 "\n", (int32_t)g->tail);
+	fprintf(f, "  cir_mask=0x%" PRIx32 "\n", g->cir_mask);
+	fprintf(f, "  nb_nodes=%" PRId32 "\n", g->nb_nodes);
+	fprintf(f, "  socket=%d\n", g->socket);
+	fprintf(f, "  fence=0x%" PRIx64 "\n", g->fence);
+	fprintf(f, "  nodes_start=0x%" PRIx32 "\n", g->nodes_start);
+	fprintf(f, "  cir_start=%p\n", g->cir_start);
+
+	rte_graph_foreach_node(count, off, g, n) {
+		if (!all && n->idx == 0)
+			continue;
+		fprintf(f, "     node[%d] <%s>\n", count, n->name);
+		fprintf(f, "       fence=0x%" PRIx64 "\n", n->fence);
+		fprintf(f, "       objs=%p\n", n->objs);
+		fprintf(f, "       process=%p\n", n->process);
+		fprintf(f, "       id=0x%" PRIx32 "\n", n->id);
+		fprintf(f, "       offset=0x%" PRIx32 "\n", n->off);
+		fprintf(f, "       nb_edges=%" PRId32 "\n", n->nb_edges);
+		fprintf(f, "       realloc_count=%d\n", n->realloc_count);
+		fprintf(f, "       size=%d\n", n->size);
+		fprintf(f, "       idx=%d\n", n->idx);
+		fprintf(f, "       total_objs=%" PRId64 "\n", n->total_objs);
+		fprintf(f, "       total_calls=%" PRId64 "\n", n->total_calls);
+		for (i = 0; i < n->nb_edges; i++)
+			fprintf(f, "          edge[%d] <%s>\n", i,
+				n->nodes[i]->name);
+	}
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 051fad53a..397247797 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -318,6 +318,19 @@ struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
 struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
 					const char *node_name);
 
+/* Debug functions */
+/**
+ * @internal
+ *
+ * Dump internal graph object data.
+ *
+ * @param f
+ *   FILE pointer to dump the data.
+ * @param g
+ *   Pointer to the internal graph object.
+ */
+void graph_dump(FILE *f, struct graph *g);
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 2797be044..851f4772e 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_dump;
 	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
@@ -14,6 +15,7 @@ EXPERIMENTAL {
 	rte_graph_max_count;
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
+	rte_graph_obj_dump;
 
 	rte_node_clone;
 	rte_node_dump;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 11/26] graph: implement stats support
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (9 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 10/26] graph: implement debug routines jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 12/26] graph: implement fastpath API routines jerinj
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph stats collection API. This API will
create a cluster for a specified node pattern and aggregate the node
runtime stats.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph_stats.c         | 406 +++++++++++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/rte_graph_version.map |   5 +
 4 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_stats.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 7bfd7d51f..967c8d9bc 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,6 +18,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_stats.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
diff --git a/lib/librte_graph/graph_stats.c b/lib/librte_graph/graph_stats.c
new file mode 100644
index 000000000..125e08d73
--- /dev/null
+++ b/lib/librte_graph/graph_stats.c
@@ -0,0 +1,406 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+
+#include "graph_private.h"
+
+/* Capture all graphs of cluster */
+struct cluster {
+	rte_graph_t nb_graphs;
+	rte_graph_t size;
+
+	struct graph **graphs;
+};
+
+/* Capture same node ID across cluster  */
+struct cluster_node {
+	struct rte_graph_cluster_node_stats stat;
+	rte_node_t nb_nodes;
+
+	struct rte_node *nodes[];
+};
+
+struct rte_graph_cluster_stats {
+	/* Header */
+	rte_graph_cluster_stats_cb_t fn;
+	uint32_t cluster_node_size; /* Size of struct cluster_node */
+	rte_node_t max_nodes;
+	int socket_id;
+	void *cookie;
+	size_t sz;
+
+	struct cluster_node clusters[];
+} __rte_cache_aligned;
+
+#define boarder()                                                              \
+	fprintf(f, "+-------------------------------+---------------+--------" \
+		   "-------+---------------+---------------+---------------+-" \
+		   "----------+\n")
+
+static inline void
+print_banner(FILE *f)
+{
+	boarder();
+	fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
+		"|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
+		"|cycles/call|");
+	boarder();
+}
+
+static inline void
+print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
+{
+	double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
+	const uint64_t prev_calls = stat->prev_calls;
+	const uint64_t prev_objs = stat->prev_objs;
+	const uint64_t cycles = stat->cycles;
+	const uint64_t calls = stat->calls;
+	const uint64_t objs = stat->objs;
+	uint64_t call_delta;
+
+	call_delta = calls - prev_calls;
+	objs_per_call =
+		call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
+	cycles_per_call =
+		call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
+			   : 0;
+	ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
+	objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
+	objs_per_sec /= 1000000;
+
+	fprintf(f,
+		"|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
+		"|%-15.3f|%-15.6f|%-11.4f|\n",
+		stat->name, calls, objs, stat->realloc_count, objs_per_call,
+		objs_per_sec, cycles_per_call);
+}
+
+static int
+graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
+		       const struct rte_graph_cluster_node_stats *stat)
+{
+	FILE *f = cookie;
+
+	if (unlikely(is_first))
+		print_banner(f);
+	if (stat->objs)
+		print_node(f, stat);
+	if (unlikely(is_last))
+		boarder();
+
+	return 0;
+};
+
+static struct rte_graph_cluster_stats *
+stats_mem_init(struct cluster *cluster,
+	       const struct rte_graph_cluster_stats_param *prm)
+{
+	size_t sz = sizeof(struct rte_graph_cluster_stats);
+	struct rte_graph_cluster_stats *stats;
+	rte_graph_cluster_stats_cb_t fn;
+	int socket_id = prm->socket_id;
+	uint32_t cluster_node_size;
+
+	/* Fix up callback */
+	fn = prm->fn;
+	if (fn == NULL)
+		fn = graph_cluster_stats_cb;
+
+	cluster_node_size = sizeof(struct cluster_node);
+	/* For a given cluster, max nodes will be the max number of graphs */
+	cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
+	cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
+
+	stats = realloc(NULL, sz);
+	memset(stats, 0, sz);
+	if (stats) {
+		stats->fn = fn;
+		stats->cluster_node_size = cluster_node_size;
+		stats->max_nodes = 0;
+		stats->socket_id = socket_id;
+		stats->cookie = prm->cookie;
+		stats->sz = sz;
+	}
+
+	return stats;
+}
+
+static int
+stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
+		   struct rte_graph *graph, struct graph_node *graph_node)
+{
+	struct rte_graph_cluster_stats *stats = *stats_in;
+	rte_node_t id = graph_node->node->id;
+	struct cluster_node *cluster;
+	struct rte_node *node;
+	rte_node_t count;
+
+	cluster = stats->clusters;
+
+	/* Iterate over cluster node array to find node ID match */
+	for (count = 0; count < stats->max_nodes; count++) {
+		/* Found an existing node in the reel */
+		if (cluster->stat.id == id) {
+			node = graph_node_id_to_ptr(graph, id);
+			if (node == NULL)
+				SET_ERR_JMP(
+					ENOENT, err,
+					"Failed to find node %s in graph %s",
+					graph_node->node->name, graph->name);
+
+			cluster->nodes[cluster->nb_nodes++] = node;
+			return 0;
+		}
+		cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
+	}
+
+	/* Hey, it is a new node, allocate space for it in the reel */
+	stats = realloc(stats, stats->sz + stats->cluster_node_size);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, err, "Realloc failed");
+
+	/* Clear the new struct cluster_node area */
+	cluster = RTE_PTR_ADD(stats, stats->sz),
+	memset(cluster, 0, stats->cluster_node_size);
+	memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
+	cluster->stat.id = graph_node->node->id;
+	cluster->stat.hz = rte_get_timer_hz();
+	node = graph_node_id_to_ptr(graph, id);
+	if (node == NULL)
+		SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s",
+			    graph_node->node->name, graph->name);
+	cluster->nodes[cluster->nb_nodes++] = node;
+
+	stats->sz += stats->cluster_node_size;
+	stats->max_nodes++;
+	*stats_in = stats;
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+stats_mem_fini(struct rte_graph_cluster_stats *stats)
+{
+	free(stats);
+}
+
+static void
+cluster_init(struct cluster *cluster)
+{
+	memset(cluster, 0, sizeof(*cluster));
+}
+
+static int
+cluster_add(struct cluster *cluster, struct graph *graph)
+{
+	rte_graph_t count;
+	size_t sz;
+
+	/* Skip the if graph is already added to cluster */
+	for (count = 0; count < cluster->nb_graphs; count++)
+		if (cluster->graphs[count] == graph)
+			return 0;
+
+	/* Expand the cluster if required to store graph objects */
+	if (cluster->nb_graphs + 1 > cluster->size) {
+		cluster->size = RTE_MAX(1, cluster->size * 2);
+		sz = sizeof(struct graph *) * cluster->size;
+		cluster->graphs = realloc(cluster->graphs, sz);
+		if (cluster->graphs == NULL)
+			SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
+	}
+
+	/* Add graph to cluster */
+	cluster->graphs[cluster->nb_graphs++] = graph;
+	return 0;
+
+free:
+	return -rte_errno;
+}
+
+static void
+cluster_fini(struct cluster *cluster)
+{
+	if (cluster->graphs)
+		free(cluster->graphs);
+}
+
+static int
+expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
+{
+	struct graph_head *graph_head = graph_list_head_get();
+	struct graph *graph;
+	bool found = false;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(graph, graph_head, next) {
+		if (fnmatch(pattern, graph->name, 0) == 0) {
+			if (cluster_add(cluster, graph))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
+			    pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+struct rte_graph_cluster_stats *
+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
+{
+	struct rte_graph_cluster_stats *stats, *rc = NULL;
+	struct graph_node *graph_node;
+	struct cluster cluster;
+	struct graph *graph;
+	const char *pattern;
+	rte_graph_t i;
+
+	/* Sanity checks */
+	if (!rte_graph_has_stats_feature())
+		SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
+
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Invalid param");
+
+	if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
+		SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
+
+	cluster_init(&cluster);
+
+	graph_spinlock_lock();
+	/* Expand graph pattern and add the graph to the cluster */
+	for (i = 0; i < prm->nb_graph_patterns; i++) {
+		pattern = prm->graph_patterns[i];
+		if (expand_pattern_to_cluster(&cluster, pattern))
+			goto bad_pattern;
+	}
+
+	/* Alloc the stats memory */
+	stats = stats_mem_init(&cluster, prm);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
+
+	/* Iterate over M(Graph) x N (Nodes in graph) */
+	for (i = 0; i < cluster.nb_graphs; i++) {
+		graph = cluster.graphs[i];
+		STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+			struct rte_graph *graph_fp = graph->graph;
+			if (stats_mem_populate(&stats, graph_fp, graph_node))
+				goto realloc_fail;
+		}
+	}
+
+	/* Finally copy to hugepage memory to avoid pressure on rte_realloc */
+	rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
+	if (rc)
+		rte_memcpy(rc, stats, stats->sz);
+	else
+		SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
+
+realloc_fail:
+	stats_mem_fini(stats);
+bad_pattern:
+	graph_spinlock_unlock();
+	cluster_fini(&cluster);
+fail:
+	return rc;
+}
+
+void
+rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
+{
+	return rte_free(stat);
+}
+
+static inline void
+cluster_node_arregate_stats(struct cluster_node *cluster)
+{
+	uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+	struct rte_node *node;
+	rte_node_t count;
+
+	for (count = 0; count < cluster->nb_nodes; count++) {
+		node = cluster->nodes[count];
+
+		calls += node->total_calls;
+		objs += node->total_objs;
+		cycles += node->total_cycles;
+		realloc_count += node->realloc_count;
+	}
+
+	stat->calls = calls;
+	stat->objs = objs;
+	stat->cycles = cycles;
+	stat->ts = rte_get_timer_cycles();
+	stat->realloc_count = realloc_count;
+}
+
+static inline void
+cluster_node_store_prev_stats(struct cluster_node *cluster)
+{
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+
+	stat->prev_ts = stat->ts;
+	stat->prev_calls = stat->calls;
+	stat->prev_objs = stat->objs;
+	stat->prev_cycles = stat->cycles;
+}
+
+void
+rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+	int rc = 0;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		cluster_node_arregate_stats(cluster);
+		if (!skip_cb)
+			rc = stat->fn(!count, (count == stat->max_nodes - 1),
+				      stat->cookie, &cluster->stat);
+		cluster_node_store_prev_stats(cluster);
+		if (rc)
+			break;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
+
+void
+rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		struct rte_graph_cluster_node_stats *node = &cluster->stat;
+
+		node->ts = 0;
+		node->calls = 0;
+		node->objs = 0;
+		node->cycles = 0;
+		node->prev_ts = 0;
+		node->prev_calls = 0;
+		node->prev_objs = 0;
+		node->prev_cycles = 0;
+		node->realloc_count = 0;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index fb203a5e2..929a17f84 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_stats.c', 'graph_populate.c')
 headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 851f4772e..adf55d406 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -17,6 +17,11 @@ EXPERIMENTAL {
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
 
+	rte_graph_cluster_stats_create;
+	rte_graph_cluster_stats_destroy;
+	rte_graph_cluster_stats_get;
+	rte_graph_cluster_stats_reset;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 12/26] graph: implement fastpath API routines
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (10 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 11/26] graph: implement stats support jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 13/26] graph: add unit test case jerinj
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for rte_graph_walk() API. This will perform a walk
on the circular buffer and call the process function of each node
and collect the stats if stats collection is enabled.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md              |   1 +
 lib/librte_graph/graph.c               |  16 +
 lib/librte_graph/rte_graph_version.map |  10 +
 lib/librte_graph/rte_graph_worker.h    | 434 +++++++++++++++++++++++++
 4 files changed, 461 insertions(+)

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5cc50f750..fd2ff64d7 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -160,6 +160,7 @@ The public API headers are grouped by topics:
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
+    [graph_worker]     (@ref rte_graph_worker.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index cc1e523d9..78bc83c4e 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -475,6 +475,22 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+void __rte_noinline
+__rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,
+			     uint16_t req_size)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
+
 static int
 graph_to_dot(FILE *f, struct graph *graph)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index adf55d406..13b838752 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 	__rte_node_stream_alloc;
+	__rte_node_stream_alloc_size;
 
 	rte_graph_create;
 	rte_graph_destroy;
@@ -16,6 +17,7 @@ EXPERIMENTAL {
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
+	rte_graph_walk;
 
 	rte_graph_cluster_stats_create;
 	rte_graph_cluster_stats_destroy;
@@ -28,10 +30,18 @@ EXPERIMENTAL {
 	rte_node_edge_get;
 	rte_node_edge_shrink;
 	rte_node_edge_update;
+	rte_node_enqueue;
+	rte_node_enqueue_x1;
+	rte_node_enqueue_x2;
+	rte_node_enqueue_x4;
+	rte_node_enqueue_next;
 	rte_node_from_name;
 	rte_node_id_to_name;
 	rte_node_list_dump;
 	rte_node_max_count;
+	rte_node_next_stream_get;
+	rte_node_next_stream_put;
+	rte_node_next_stream_move;
 
 	local: *;
 };
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
index a7c780d4d..8e067e673 100644
--- a/lib/librte_graph/rte_graph_worker.h
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -100,6 +100,440 @@ struct rte_node {
 __rte_experimental
 void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
 
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream with requested number of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param req_size
+ *   Number of objects to be allocated.
+ */
+__rte_experimental
+void __rte_node_stream_alloc_size(struct rte_graph *graph,
+				  struct rte_node *node, uint16_t req_size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Perform graph walk on the circular buffer and invoke the process function
+ * of the nodes and collect the stats.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup function.
+ *
+ * @see rte_graph_lookup()
+ */
+__rte_experimental
+static inline void
+rte_graph_walk(struct rte_graph *graph)
+{
+	const rte_graph_off_t *cir_start = graph->cir_start;
+	const rte_node_t mask = graph->cir_mask;
+	uint32_t head = graph->head;
+	struct rte_node *node;
+	uint64_t start;
+	uint16_t rc;
+	void **objs;
+
+	/*
+	 * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
+	 * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
+	 * in a circular buffer fashion.
+	 *
+	 *	+-----+ <= cir_start - head [number of source nodes]
+	 *	|     |
+	 *	| ... | <= source nodes
+	 *	|     |
+	 *	+-----+ <= cir_start [head = 0] [tail = 0]
+	 *	|     |
+	 *	| ... | <= pending streams
+	 *	|     |
+	 *	+-----+ <= cir_start + mask
+	 */
+	while (likely(head != graph->tail)) {
+		node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
+		RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+		objs = node->objs;
+		rte_prefetch0(objs);
+
+		if (rte_graph_has_stats_feature()) {
+			start = rte_rdtsc();
+			rc = node->process(graph, node, objs, node->idx);
+			node->total_cycles += rte_rdtsc() - start;
+			node->total_calls++;
+			node->total_objs += rc;
+		} else {
+			node->process(graph, node, objs, node->idx);
+		}
+		node->idx = 0;
+		head = likely((int32_t)head > 0) ? head & mask : head;
+	}
+	graph->tail = 0;
+}
+
+/* Fast path helper functions */
+
+/**
+ * @internal
+ *
+ * Enqueue a given node to the tail of the graph reel.
+ *
+ * @param graph
+ *   Pointer Graph object.
+ * @param node
+ *   Pointer to node object to be enqueued.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_tail_update(struct rte_graph *graph, struct rte_node *node)
+{
+	uint32_t tail;
+
+	tail = graph->tail;
+	graph->cir_start[tail++] = node->off;
+	graph->tail = tail & graph->cir_mask;
+}
+
+/**
+ * @internal
+ *
+ * Enqueue sequence prologue function.
+ *
+ * Updates the node to tail of graph reel and resizes the number of objects
+ * available in the stream as needed.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param idx
+ *   Index at which the object enqueue starts from.
+ * @param space
+ *   Space required for the object enqueue.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_prologue(struct rte_graph *graph, struct rte_node *node,
+			    const uint16_t idx, const uint16_t space)
+{
+
+	/* Add to the pending stream list if the node is new */
+	if (idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	if (unlikely(node->size < (idx + space)))
+		__rte_node_stream_alloc(graph, node);
+}
+
+/**
+ * @internal
+ *
+ * Get the node pointer from current node edge id.
+ *
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Edge id of the required node.
+ *
+ * @return
+ *   Pointer to the node denoted by the edge id.
+ */
+static __rte_always_inline struct rte_node *
+__rte_node_next_node_get(struct rte_node *node, rte_edge_t next)
+{
+	RTE_ASSERT(next < node->nb_edges);
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+	node = node->nodes[next];
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+
+	return node;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue the objs to next node for further processing and set
+ * the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param objs
+ *   Objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
+		 rte_edge_t next, void **objs, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, nb_objs);
+
+	rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
+	node->idx = idx + nb_objs;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only one obj to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x1(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 1);
+
+	node->objs[idx++] = obj;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only two objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue two objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   Obj to enqueue.
+ * @param obj1
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x2(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 2);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only four objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue four objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   1st obj to enqueue.
+ * @param obj1
+ *   2nd obj to enqueue.
+ * @param obj2
+ *   3rd obj to enqueue.
+ * @param obj3
+ *   4th obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x4(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1, void *obj2,
+		    void *obj3)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 4);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->objs[idx++] = obj2;
+	node->objs[idx++] = obj3;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue objs to multiple next nodes for further processing and
+ * set the next nodes to pending state in the circular buffer.
+ * objs[i] will be enqueued to nexts[i].
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param nexts
+ *   List of relative next node indices to enqueue objs.
+ * @param objs
+ *   List of objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
+		      rte_edge_t *nexts, void **objs, uint16_t nb_objs)
+{
+	uint16_t i;
+
+	for (i = 0; i < nb_objs; i++)
+		rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the stream of next node to enqueue the objs.
+ * Once done with the updating the objs, needs to call
+ * rte_node_next_stream_put to put the next node to pending state.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to get stream.
+ * @param nb_objs
+ *   Requested free size of the next stream.
+ *
+ * @return
+ *   Valid next stream on success.
+ *
+ * @see rte_node_next_stream_put().
+ */
+__rte_experimental
+static inline void **
+rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+	uint16_t free_space = node->size - idx;
+
+	if (unlikely(free_space < nb_objs))
+		__rte_node_stream_alloc_size(graph, node, nb_objs);
+
+	return &node->objs[idx];
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Put the next stream to pending state in the circular buffer
+ * for further processing. Should be invoked followed by
+ * rte_node_next_stream_get().
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index..
+ * @param idx
+ *   Number of objs updated in the stream after getting the stream using
+ *   rte_node_next_stream_get.
+ *
+ * @see rte_node_next_stream_get().
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t idx)
+{
+	if (unlikely(!idx))
+		return;
+
+	node = __rte_node_next_node_get(node, next);
+	if (node->idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	node->idx += idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Home run scenario, Enqueue all the objs of current node to next
+ * node in optimized way by swapping the streams of both nodes.
+ * Performs good when next node is already not in pending state.
+ * If next node is already in pending state then normal enqueue
+ * will be used.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param src
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index.
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_move(struct rte_graph *graph, struct rte_node *src,
+			  rte_edge_t next)
+{
+	struct rte_node *dst = __rte_node_next_node_get(src, next);
+
+	/* Let swap the pointers if dst don't have valid objs */
+	if (likely(dst->idx == 0)) {
+		void **dobjs = dst->objs;
+		uint16_t dsz = dst->size;
+		dst->objs = src->objs;
+		dst->size = src->size;
+		src->objs = dobjs;
+		src->size = dsz;
+		dst->idx = src->idx;
+		__rte_node_enqueue_tail_update(graph, dst);
+	} else { /* Move the objects from src node to dst node */
+		rte_node_enqueue(graph, src, next, src->objs, src->idx);
+	}
+}
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 13/26] graph: add unit test case
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (11 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 12/26] graph: implement fastpath API routines jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 14/26] graph: add performance testcase jerinj
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram

From: Kiran Kumar K <kirankumark@marvell.com>

Adding the unit test to test the functionality of node and graph APIs.
Testing includes registering a node, cloning a node, creating a graph,
perform graph walk, collecting stats and all node and graph debug APIs.

example command to test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 app/test/Makefile     |   4 +
 app/test/meson.build  |  10 +-
 app/test/test_graph.c | 820 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 832 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_graph.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 1f080d162..065582916 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -221,6 +221,10 @@ SRCS-y += test_event_timer_adapter.c
 SRCS-y += test_event_crypto_adapter.c
 endif
 
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
+SRCS-y += test_graph.c
+endif
+
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
 SRCS-y += test_rawdev.c
 endif
diff --git a/app/test/meson.build b/app/test/meson.build
index 0a2ce710f..855403932 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -51,6 +51,7 @@ test_sources = files('commands.c',
 	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
+	'test_graph.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
@@ -151,7 +152,9 @@ test_deps = ['acl',
 	'rib',
 	'ring',
 	'stack',
-	'timer'
+	'timer',
+	'graph',
+	'node'
 ]
 
 fast_test_names = [
@@ -183,6 +186,7 @@ fast_test_names = [
         'fib6_autotest',
         'func_reentrancy_autotest',
         'flow_classify_autotest',
+	'graph_autotest',
         'hash_autotest',
         'interrupt_autotest',
         'logs_autotest',
@@ -383,13 +387,15 @@ endforeach
 test_dep_objs += cc.find_library('execinfo', required: false)
 
 link_libs = []
+link_nodes = []
 if get_option('default_library') == 'static'
 	link_libs = dpdk_drivers
+	link_nodes = dpdk_graph_nodes
 endif
 
 dpdk_test = executable('dpdk-test',
 	test_sources,
-	link_whole: link_libs,
+	link_whole: link_libs + link_nodes,
 	dependencies: test_dep_objs,
 	c_args: [cflags, '-DALLOW_EXPERIMENTAL_API'],
 	install_rpath: driver_install_path,
diff --git a/app/test/test_graph.c b/app/test/test_graph.c
new file mode 100644
index 000000000..e0116a9ce
--- /dev/null
+++ b/app/test/test_graph.c
@@ -0,0 +1,820 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static uint16_t test_node_worker_source(struct rte_graph *graph,
+					struct rte_node *node, void **objs,
+					uint16_t nb_objs);
+
+static uint16_t test_node0_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node1_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node2_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node3_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+#define MBUFF_SIZE 512
+#define MAX_NODES  4
+
+static struct rte_mbuf mbuf[MAX_NODES + 1][MBUFF_SIZE];
+static void *mbuf_p[MAX_NODES + 1][MBUFF_SIZE];
+static rte_graph_t graph_id;
+static uint64_t obj_stats[MAX_NODES + 1];
+static uint64_t fn_calls[MAX_NODES + 1];
+
+const char *node_patterns[] = {
+	"test_node_source1",	   "test_node00",
+	"test_node00-test_node11", "test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+const char *node_names[] = {
+	"test_node00",
+	"test_node00-test_node11",
+	"test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+struct test_node_register {
+#define NODE_NAMESIZE 64
+	char name[NODE_NAMESIZE];
+	rte_node_process_t process;
+	uint16_t nb_edges;
+	const char *next_nodes[MAX_NODES];
+};
+
+typedef struct {
+	uint32_t idx;
+	struct test_node_register node;
+} test_node_t;
+
+typedef struct {
+	test_node_t test_node[MAX_NODES];
+} test_main_t;
+
+static test_main_t test_main = {
+	.test_node = {
+		{
+			.node = {
+					.name = "test_node00",
+					.process = test_node0_worker,
+					.nb_edges = 2,
+					.next_nodes = {"test_node00-"
+						       "test_node11",
+						       "test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node11",
+					.process = test_node1_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node22",
+					.process = test_node2_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node33"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node33",
+					.process = test_node3_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00"},
+				},
+		},
+	},
+};
+
+static int
+node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	*(uint32_t *)node->ctx = node->id;
+
+	return 0;
+}
+
+static struct rte_node_register test_node_source = {
+	.name = "test_node_source1",
+	.process = test_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.nb_edges = 2,
+	.init = node_init,
+	.next_nodes = {"test_node00", "test_node00-test_node11"},
+};
+RTE_NODE_REGISTER(test_node_source);
+
+static struct rte_node_register test_node0 = {
+	.name = "test_node00",
+	.process = test_node0_worker,
+	.init = node_init,
+};
+RTE_NODE_REGISTER(test_node0);
+
+uint16_t
+test_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	uint32_t obj_node0 = rand() % 100, obj_node1;
+	test_main_t *tm = &test_main;
+	struct rte_mbuf *data;
+	void **next_stream;
+	rte_node_t next;
+	uint32_t i;
+
+	RTE_SET_USED(objs);
+	nb_objs = RTE_GRAPH_BURST_SIZE;
+
+	/* Prepare stream for next node 0 */
+	obj_node0 = nb_objs * obj_node0 * 0.01;
+	next = 0;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node0);
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[0][i];
+		data->udata64 = ((uint64_t)tm->test_node[0].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][i];
+	}
+	rte_node_next_stream_put(graph, node, next, obj_node0);
+
+	/* Prepare stream for next node 1 */
+	obj_node1 = nb_objs - obj_node0;
+	next = 1;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node1);
+	for (i = 0; i < obj_node1; i++) {
+		data = &mbuf[0][obj_node0 + i];
+		data->udata64 = ((uint64_t)tm->test_node[1].idx << 32) | i;
+		if ((i + 1) == obj_node1)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][obj_node0 + i];
+	}
+
+	rte_node_next_stream_put(graph, node, next, obj_node1);
+	obj_stats[0] += nb_objs;
+	fn_calls[0] += 1;
+	return nb_objs;
+}
+
+uint16_t
+test_node0_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+
+	if (*(uint32_t *)node->ctx == test_node0.id) {
+		uint32_t obj_node0 = rand() % 100, obj_node1;
+		struct rte_mbuf *data;
+		uint8_t second_pass = 0;
+		uint32_t count = 0;
+		uint32_t i;
+
+		obj_stats[1] += nb_objs;
+		fn_calls[1] += 1;
+
+		for (i = 0; i < nb_objs; i++) {
+			data = (struct rte_mbuf *)objs[i];
+			if ((data->udata64 >> 32) != tm->test_node[0].idx) {
+				printf("Data idx miss match at node 0, expected"
+				       " = %u got = %u\n",
+				       tm->test_node[0].idx,
+				       (uint32_t)(data->udata64 >> 32));
+				goto end;
+			}
+
+			if ((data->udata64 & 0xffff) != (i - count)) {
+				printf("Expected buff count miss match at "
+				       "node 0\n");
+				goto end;
+			}
+
+			if (data->udata64 & (0x1 << 16))
+				count = i + 1;
+			if (data->udata64 & (0x1 << 17))
+				second_pass = 1;
+		}
+
+		if (count != i) {
+			printf("Count mismatch at node 0\n");
+			goto end;
+		}
+
+		obj_node0 = nb_objs * obj_node0 * 0.01;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[1][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[1].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[1][0],
+				 obj_node0);
+
+		obj_node1 = nb_objs - obj_node0;
+		for (i = 0; i < obj_node1; i++) {
+			data = &mbuf[1][obj_node0 + i];
+			data->udata64 =
+				((uint64_t)tm->test_node[2].idx << 32) | i;
+			if ((i + 1) == obj_node1)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 1, (void **)&mbuf_p[1][obj_node0],
+				 obj_node1);
+
+	} else if (*(uint32_t *)node->ctx == tm->test_node[1].idx) {
+		test_node1_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[2].idx) {
+		test_node2_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[3].idx) {
+		test_node3_worker(graph, node, objs, nb_objs);
+	} else {
+		printf("Unexpected node context\n");
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node1_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	uint32_t obj_node0 = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t i;
+
+	obj_stats[2] += nb_objs;
+	fn_calls[2] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[1].idx) {
+			printf("Data idx miss match at node 1, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[1].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 1\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 1\n");
+		goto end;
+	}
+
+	obj_node0 = nb_objs;
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[2][i];
+		data->udata64 = ((uint64_t)tm->test_node[2].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		if (second_pass)
+			data->udata64 |= (1 << 17);
+	}
+	rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[2][0], obj_node0);
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node2_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[3] += nb_objs;
+	fn_calls[3] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[2].idx) {
+			printf("Data idx miss match at node 2, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[2].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 2\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 2\n");
+		goto end;
+	}
+
+	if (!second_pass) {
+		obj_node0 = nb_objs;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[3][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[3].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[3][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node3_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[4] += nb_objs;
+	fn_calls[4] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[3].idx) {
+			printf("Data idx miss match at node 3, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[3].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 3\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 3\n");
+		goto end;
+	}
+
+	if (second_pass) {
+		printf("Unexpected buffers are at node 3\n");
+		goto end;
+	} else {
+		obj_node0 = nb_objs * 2;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[4][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[0].idx << 32) | i;
+			data->udata64 |= (1 << 17);
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[4][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+static int
+test_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	int i;
+
+	/* Verify the name with ID */
+	for (i = 1; i < MAX_NODES; i++) {
+		char *name = rte_node_id_to_name(tm->test_node[i].idx);
+		if (strcmp(name, node_names[i]) != 0) {
+			printf("Test node name verify by ID = %d failed "
+			       "Expected = %s, got %s\n",
+			       i, node_names[i], name);
+			return -1;
+		}
+	}
+
+	/* Verify by name */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t idx = rte_node_from_name(node_names[i]);
+		if (idx != tm->test_node[i].idx) {
+			printf("Test node ID verify by name = %s failed "
+			       "Expected = %d, got %d\n",
+			       node_names[i], tm->test_node[i].idx, idx);
+			return -1;
+		}
+	}
+
+	/* Verify edge count */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t count = rte_node_edge_count(tm->test_node[i].idx);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+	}
+
+	/* Verify edge names */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t j, count;
+		char **next_edges;
+
+		count = rte_node_edge_get(tm->test_node[i].idx, NULL);
+		if (count != tm->test_node[i].node.nb_edges * sizeof(char *)) {
+			printf("Test number of edge count for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+		next_edges = malloc(count);
+		count = rte_node_edge_get(tm->test_node[i].idx, next_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		for (j = 0; j < count; j++) {
+			if (strcmp(next_edges[j],
+				   tm->test_node[i].node.next_nodes[j]) != 0) {
+				printf("Edge name miss match, expected = %s got = %s\n",
+				       tm->test_node[i].node.next_nodes[j],
+				       next_edges[j]);
+				return -1;
+			}
+		}
+		free(next_edges);
+	}
+
+	return 0;
+}
+
+static int
+test_node_clone(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id, dummy_id;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	tm->test_node[0].idx = node_id;
+
+	/* Clone with same name, should fail */
+	dummy_id = rte_node_clone(node_id, "test_node00");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid id when clone with same name, Expecting fail\n");
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		tm->test_node[i].idx =
+			rte_node_clone(node_id, tm->test_node[i].node.name);
+		if (rte_node_is_invalid(tm->test_node[i].idx)) {
+			printf("Got invalid node id\n");
+			return -1;
+		}
+	}
+
+	/* Clone from cloned node should fail */
+	dummy_id = rte_node_clone(tm->test_node[1].idx, "dummy_node");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid node id when cloning from cloned node, expected fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_update_edges(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id;
+	uint16_t count;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	count = rte_node_edge_update(node_id, 0,
+				     tm->test_node[0].node.next_nodes,
+				     tm->test_node[0].node.nb_edges);
+	if (count != tm->test_node[0].node.nb_edges) {
+		printf("Update edges failed expected: %d got = %d\n",
+		       tm->test_node[0].node.nb_edges, count);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		count = rte_node_edge_update(tm->test_node[i].idx, 0,
+					     tm->test_node[i].node.next_nodes,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Update edges failed expected: %d got = %d\n",
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		count = rte_node_edge_shrink(tm->test_node[i].idx,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Shrink edges failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_create_graph(void)
+{
+	static const char *node_patterns_dummy[] = {
+		"test_node_source1",	   "test_node00",
+		"test_node00-test_node11", "test_node00-test_node22",
+		"test_node00-test_node33", "test_node00-dummy_node",
+	};
+	struct rte_graph_param gconf = {
+		.socket_id = SOCKET_ID_ANY,
+		.nb_node_patterns = 6,
+		.node_patterns = node_patterns_dummy,
+	};
+	uint32_t dummy_node_id;
+	uint32_t node_id;
+
+	node_id = rte_node_from_name("test_node00");
+	dummy_node_id = rte_node_clone(node_id, "dummy_node");
+	if (rte_node_is_invalid(dummy_node_id)) {
+		printf("Got invalid node id\n");
+		return -1;
+	}
+
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id != RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation success with isolated node, expected graph creation fail\n");
+		return -1;
+	}
+
+	gconf.nb_node_patterns = 5;
+	gconf.node_patterns = node_patterns;
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+test_graph_walk(void)
+{
+	struct rte_graph *graph = rte_graph_lookup("worker0");
+	int i;
+
+	if (!graph) {
+		printf("Graph lookup failed\n");
+		return -1;
+	}
+
+	for (i = 0; i < 5; i++)
+		rte_graph_walk(graph);
+	return 0;
+}
+
+static int
+test_graph_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	struct rte_node *node;
+	int i;
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get(graph_id, tm->test_node[i].idx);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get_by_name("worker0", node_names[i]);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+graph_cluster_stats_cb_t(bool is_first, bool is_last, void *cookie,
+			 const struct rte_graph_cluster_node_stats *st)
+{
+	int i;
+
+	RTE_SET_USED(is_first);
+	RTE_SET_USED(is_last);
+	RTE_SET_USED(cookie);
+
+	for (i = 0; i < MAX_NODES + 1; i++) {
+		rte_node_t id = rte_node_from_name(node_patterns[i]);
+		if (id == st->id) {
+			if (obj_stats[i] != st->objs) {
+				printf("Obj count miss match for node = %s expected = %"PRId64", got=%"PRId64"\n",
+				       node_patterns[i], obj_stats[i],
+				       st->objs);
+				return -1;
+			}
+
+			if (fn_calls[i] != st->calls) {
+				printf("Func call miss match for node = %s expected = %"PRId64", got = %"PRId64"\n",
+				       node_patterns[i], fn_calls[i],
+				       st->calls);
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+test_print_stats(void)
+{
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker0";
+
+	if (!rte_graph_has_stats_feature())
+		return 0;
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+	s_param.fn = graph_cluster_stats_cb_t;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL) {
+		printf("Unable to get stats\n");
+		return -1;
+	}
+	/* Clear screen and move to top left */
+	rte_graph_cluster_stats_get(stats, 0);
+	rte_graph_cluster_stats_destroy(stats);
+
+	return 0;
+}
+
+static int
+graph_setup(void)
+{
+	int i, j;
+
+	for (i = 0; i <= MAX_NODES; i++) {
+		for (j = 0; j < MBUFF_SIZE; j++)
+			mbuf_p[i][j] = &mbuf[i][j];
+	}
+	if (test_node_clone()) {
+		printf("test_node_clone: fail\n");
+		return -1;
+	}
+	printf("test_node_clone: pass\n");
+
+	return 0;
+}
+
+static void
+graph_teardown(void)
+{
+	rte_graph_t id;
+
+	id = rte_graph_destroy("worker0");
+	if (id == RTE_GRAPH_ID_INVALID)
+		printf("Graph Destroy failed\n");
+}
+
+static struct unit_test_suite graph_testsuite = {
+	.suite_name = "Graph library test suite",
+	.setup = graph_setup,
+	.teardown = graph_teardown,
+	.unit_test_cases = {
+		TEST_CASE(test_update_edges),
+		TEST_CASE(test_lookup_functions),
+		TEST_CASE(test_create_graph),
+		TEST_CASE(test_graph_lookup_functions),
+		TEST_CASE(test_graph_walk),
+		TEST_CASE(test_print_stats),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+graph_autotest_fn(void)
+{
+	return unit_test_suite_runner(&graph_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_autotest, graph_autotest_fn);
+
+static int
+test_node_list_dump(void)
+{
+	rte_node_list_dump(stdout);
+
+	return TEST_SUCCESS;
+}
+REGISTER_TEST_COMMAND(node_list_dump, test_node_list_dump);
+
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 14/26] graph: add performance testcase
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (12 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 13/26] graph: add unit test case jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 15/26] node: add log infra and null node jerinj
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add unit test framework to create and test performance of various graph
models.

example command to test:

echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 app/test/Makefile          |    1 +
 app/test/meson.build       |    1 +
 app/test/test_graph_perf.c | 1057 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1059 insertions(+)
 create mode 100644 app/test/test_graph_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 065582916..cd2801427 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -223,6 +223,7 @@ endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
 SRCS-y += test_graph.c
+SRCS-y += test_graph_perf.c
 endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
diff --git a/app/test/meson.build b/app/test/meson.build
index 855403932..3fe155778 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -52,6 +52,7 @@ test_sources = files('commands.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_graph.c',
+	'test_graph_perf.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
diff --git a/app/test/test_graph_perf.c b/app/test/test_graph_perf.c
new file mode 100644
index 000000000..a629f1e35
--- /dev/null
+++ b/app/test/test_graph_perf.c
@@ -0,0 +1,1057 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_lcore.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+#define TEST_GRAPH_PERF_MZ	     "graph_perf_data"
+#define TEST_GRAPH_SRC_NAME	     "test_graph_perf_source"
+#define TEST_GRAPH_SRC_BRST_ONE_NAME "test_graph_perf_source_one"
+#define TEST_GRAPH_WRK_NAME	     "test_graph_perf_worker"
+#define TEST_GRAPH_SNK_NAME	     "test_graph_perf_sink"
+
+#define SOURCES(map)	     RTE_DIM(map)
+#define STAGES(map)	     RTE_DIM(map)
+#define NODES_PER_STAGE(map) RTE_DIM(map[0])
+#define SINKS(map)	     RTE_DIM(map[0])
+
+#define MAX_EDGES_PER_NODE 7
+
+struct test_node_data {
+	uint8_t node_id;
+	uint8_t is_sink;
+	uint8_t next_nodes[MAX_EDGES_PER_NODE];
+	uint8_t next_percentage[MAX_EDGES_PER_NODE];
+};
+
+struct test_graph_perf {
+	uint16_t nb_nodes;
+	rte_graph_t graph_id;
+	struct test_node_data *node_data;
+};
+
+struct graph_lcore_data {
+	uint8_t done;
+	rte_graph_t graph_id;
+};
+
+static struct test_node_data *
+graph_get_node_data(struct test_graph_perf *graph_data, rte_node_t id)
+{
+	struct test_node_data *node_data = NULL;
+	int i;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		if (graph_data->node_data[i].node_id == id) {
+			node_data = &graph_data->node_data[i];
+			break;
+		}
+
+	return node_data;
+}
+
+static int
+test_node_ctx_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	struct test_graph_perf *graph_data;
+	struct test_node_data *node_data;
+	const struct rte_memzone *mz;
+	rte_node_t nid = node->id;
+	rte_edge_t edge = 0;
+	int i;
+
+	RTE_SET_USED(graph);
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+	node_data = graph_get_node_data(graph_data, nid);
+	node->ctx[0] = node->nb_edges;
+	for (i = 0; i < node->nb_edges && !node_data->is_sink; i++, edge++) {
+		node->ctx[i + 1] = edge;
+		node->ctx[i + 9] = node_data->next_percentage[i];
+	}
+
+	return 0;
+}
+
+/* Source node function */
+static uint16_t
+test_perf_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			     void **objs, uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9] * RTE_GRAPH_BURST_SIZE) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return RTE_GRAPH_BURST_SIZE;
+}
+
+static struct rte_node_register test_graph_perf_source = {
+	.name = TEST_GRAPH_SRC_NAME,
+	.process = test_perf_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source);
+
+static uint16_t
+test_perf_node_worker_source_burst_one(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9]) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return 1;
+}
+
+static struct rte_node_register test_graph_perf_source_burst_one = {
+	.name = TEST_GRAPH_SRC_BRST_ONE_NAME,
+	.process = test_perf_node_worker_source_burst_one,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source_burst_one);
+
+/* Worker node function */
+static uint16_t
+test_perf_node_worker(struct rte_graph *graph, struct rte_node *node,
+		      void **objs, uint16_t nb_objs)
+{
+	uint16_t next = 0;
+	uint16_t enq = 0;
+	uint16_t count;
+	int i;
+
+	/* Move stream for single next node */
+	if (node->ctx[0] == 1) {
+		rte_node_next_stream_move(graph, node, node->ctx[1]);
+		return nb_objs;
+	}
+
+	/* Enqueue objects to next nodes proportionally */
+	for (i = 0; i < node->ctx[0]; i++) {
+		next = node->ctx[i + 1];
+		count = (node->ctx[i + 9] * nb_objs) / 100;
+		enq += count;
+		while (count) {
+			switch (count & (4 - 1)) {
+			case 0:
+				rte_node_enqueue_x4(graph, node, next, objs[0],
+						    objs[1], objs[2], objs[3]);
+				objs += 4;
+				count -= 4;
+				break;
+			case 1:
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 1;
+				count -= 1;
+				break;
+			case 2:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				objs += 2;
+				count -= 2;
+				break;
+			case 3:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 3;
+				count -= 3;
+				break;
+			}
+		}
+	}
+
+	if (enq != nb_objs)
+		rte_node_enqueue(graph, node, next, objs, nb_objs - enq);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_worker = {
+	.name = TEST_GRAPH_WRK_NAME,
+	.process = test_perf_node_worker,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_worker);
+
+/* Last node in graph a.k.a sink node */
+static uint16_t
+test_perf_node_sink(struct rte_graph *graph, struct rte_node *node, void **objs,
+		    uint16_t nb_objs)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_sink = {
+	.name = TEST_GRAPH_SNK_NAME,
+	.process = test_perf_node_sink,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_sink);
+
+static int
+graph_perf_setup(void)
+{
+	if (rte_lcore_count() < 2) {
+		printf("Test requires at least 2 lcores\n");
+		return TEST_SKIPPED;
+	}
+
+	return 0;
+}
+
+static void
+graph_perf_teardown(void)
+{
+}
+
+static inline rte_node_t
+graph_node_get(const char *pname, char *nname)
+{
+	rte_node_t pnode_id = rte_node_from_name(pname);
+	char lookup_name[RTE_NODE_NAMESIZE];
+	rte_node_t node_id;
+
+	snprintf(lookup_name, RTE_NODE_NAMESIZE, "%s-%s", pname, nname);
+	node_id = rte_node_from_name(lookup_name);
+
+	if (node_id != RTE_NODE_ID_INVALID) {
+		if (rte_node_edge_count(node_id))
+			rte_node_edge_shrink(node_id, 0);
+		return node_id;
+	}
+
+	return rte_node_clone(pnode_id, nname);
+}
+
+static uint16_t
+graph_node_count_edges(uint32_t stage, uint16_t node, uint16_t nodes_per_stage,
+		       uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+		       char *ename[], struct test_node_data *node_data,
+		       rte_node_t **node_map)
+{
+	uint8_t total_percent = 0;
+	uint16_t edges = 0;
+	int i;
+
+	for (i = 0; i < nodes_per_stage && edges < MAX_EDGES_PER_NODE; i++) {
+		if (edge_map[stage + 1][i][node]) {
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[stage + 1][i]));
+			node_data->next_nodes[edges] = node_map[stage + 1][i];
+			node_data->next_percentage[edges] =
+				edge_map[stage + 1][i][node];
+			edges++;
+			total_percent += edge_map[stage + 1][i][node];
+		}
+	}
+
+	if (edges >= MAX_EDGES_PER_NODE || (edges && total_percent != 100)) {
+		for (i = 0; i < edges; i++)
+			free(ename[i]);
+		return RTE_EDGE_ID_INVALID;
+	}
+
+	return edges;
+}
+
+static int
+graph_init(const char *gname, uint8_t nb_srcs, uint8_t nb_sinks,
+	   uint32_t stages, uint16_t nodes_per_stage,
+	   uint8_t src_map[][nodes_per_stage], uint8_t snk_map[][nb_sinks],
+	   uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+	   uint8_t burst_one)
+{
+	struct test_graph_perf *graph_data;
+	char nname[RTE_NODE_NAMESIZE / 2];
+	struct test_node_data *node_data;
+	char *ename[nodes_per_stage];
+	struct rte_graph_param gconf;
+	const struct rte_memzone *mz;
+	uint8_t total_percent = 0;
+	rte_node_t *src_nodes;
+	rte_node_t *snk_nodes;
+	rte_node_t **node_map;
+	char **node_patterns;
+	rte_graph_t graph_id;
+	rte_edge_t edges;
+	rte_edge_t count;
+	uint32_t i, j, k;
+
+	mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
+				 sizeof(struct test_graph_perf), 0, 0);
+	if (mz == NULL) {
+		printf("Failed to allocate graph common memory\n");
+		return -ENOMEM;
+	}
+
+	graph_data = mz->addr;
+	graph_data->nb_nodes = 0;
+	graph_data->node_data =
+		malloc(sizeof(struct test_node_data) *
+		       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (graph_data->node_data == NULL) {
+		printf("Failed to reserve memzone for graph data\n");
+		goto memzone_free;
+	}
+
+	node_patterns = malloc(sizeof(char *) *
+			       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (node_patterns == NULL) {
+		printf("Failed to reserve memory for node patterns\n");
+		goto data_free;
+	}
+
+	src_nodes = malloc(sizeof(rte_node_t) * nb_srcs);
+	if (src_nodes == NULL) {
+		printf("Failed to reserve memory for src nodes\n");
+		goto pattern_free;
+	}
+
+	snk_nodes = malloc(sizeof(rte_node_t) * nb_sinks);
+	if (snk_nodes == NULL) {
+		printf("Failed to reserve memory for snk nodes\n");
+		goto src_free;
+	}
+
+	node_map = malloc(sizeof(rte_node_t *) * stages +
+			  sizeof(rte_node_t) * nodes_per_stage * stages);
+	if (node_map == NULL) {
+		printf("Failed to reserve memory for node map\n");
+		goto snk_free;
+	}
+
+	/* Setup the Graph */
+	for (i = 0; i < stages; i++) {
+		node_map[i] =
+			(rte_node_t *)(node_map + stages) + nodes_per_stage * i;
+		for (j = 0; j < nodes_per_stage; j++) {
+			total_percent = 0;
+			for (k = 0; k < nodes_per_stage; k++)
+				total_percent += edge_map[i][j][k];
+			if (!total_percent)
+				continue;
+			node_patterns[graph_data->nb_nodes] =
+				malloc(RTE_NODE_NAMESIZE);
+			if (node_patterns[graph_data->nb_nodes] == NULL) {
+				printf("Failed to create memory for pattern\n");
+				goto pattern_name_free;
+			}
+
+			/* Clone a worker node */
+			snprintf(nname, sizeof(nname), "%d-%d", i, j);
+			node_map[i][j] =
+				graph_node_get(TEST_GRAPH_WRK_NAME, nname);
+			if (node_map[i][j] == RTE_NODE_ID_INVALID) {
+				printf("Failed to create node[%s]\n", nname);
+				graph_data->nb_nodes++;
+				goto pattern_name_free;
+			}
+			snprintf(node_patterns[graph_data->nb_nodes],
+				 RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[i][j]));
+			node_data =
+				&graph_data->node_data[graph_data->nb_nodes];
+			node_data->node_id = node_map[i][j];
+			node_data->is_sink = false;
+			graph_data->nb_nodes++;
+		}
+	}
+
+	for (i = 0; i < stages - 1; i++) {
+		for (j = 0; j < nodes_per_stage; j++) {
+			/* Count edges i.e connections of worker node to next */
+			node_data =
+				graph_get_node_data(graph_data, node_map[i][j]);
+			edges = graph_node_count_edges(i, j, nodes_per_stage,
+						       edge_map, ename,
+						       node_data, node_map);
+			if (edges == RTE_EDGE_ID_INVALID) {
+				printf("Invalid edge configuration\n");
+				goto pattern_name_free;
+			}
+			if (!edges)
+				continue;
+
+			/* Connect a node in stage 'i' to nodes
+			 * in stage 'i + 1' with edges.
+			 */
+			count = rte_node_edge_update(
+				node_map[i][j], 0,
+				(const char **)(uintptr_t)ename, edges);
+			for (k = 0; k < edges; k++)
+				free(ename[k]);
+			if (count != edges) {
+				printf("Couldn't add edges %d %d\n", edges,
+				       count);
+				goto pattern_name_free;
+			}
+		}
+	}
+
+	/* Setup Source nodes */
+	for (i = 0; i < nb_srcs; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+		/* Clone a source node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		src_nodes[i] =
+			graph_node_get(burst_one ? TEST_GRAPH_SRC_BRST_ONE_NAME
+						 : TEST_GRAPH_SRC_NAME,
+				       nname);
+		if (src_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(src_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = src_nodes[i];
+		node_data->is_sink = false;
+		graph_data->nb_nodes++;
+
+		/* Prepare next node list  to connect to */
+		for (j = 0; j < nodes_per_stage; j++) {
+			if (!src_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[0][j]));
+			node_data->next_nodes[edges] = node_map[0][j];
+			node_data->next_percentage[edges] = src_map[i][j];
+			edges++;
+			total_percent += src_map[i][j];
+		}
+
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[j]);
+			goto pattern_name_free;
+		}
+
+		/* Connect to list of next nodes using edges */
+		count = rte_node_edge_update(src_nodes[i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Setup Sink nodes */
+	for (i = 0; i < nb_sinks; i++) {
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+
+		/* Clone a sink node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		snk_nodes[i] = graph_node_get(TEST_GRAPH_SNK_NAME, nname);
+		if (snk_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(snk_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = snk_nodes[i];
+		node_data->is_sink = true;
+		graph_data->nb_nodes++;
+	}
+
+	/* Connect last stage worker nodes to sink nodes */
+	for (i = 0; i < nodes_per_stage; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_data = graph_get_node_data(graph_data,
+						node_map[stages - 1][i]);
+		/* Prepare list of sink nodes to connect to */
+		for (j = 0; j < nb_sinks; j++) {
+			if (!snk_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(snk_nodes[j]));
+			node_data->next_nodes[edges] = snk_nodes[j];
+			node_data->next_percentage[edges] = snk_map[i][j];
+			edges++;
+			total_percent += snk_map[i][j];
+		}
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[i]);
+			goto pattern_name_free;
+		}
+
+		/* Connect a worker node to a list of sink nodes */
+		count = rte_node_edge_update(node_map[stages - 1][i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Create a Graph */
+	gconf.socket_id = SOCKET_ID_ANY;
+	gconf.nb_node_patterns = graph_data->nb_nodes;
+	gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
+
+	graph_id = rte_graph_create(gname, &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		goto pattern_name_free;
+	}
+	graph_data->graph_id = graph_id;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+	free(snk_nodes);
+	free(src_nodes);
+	free(node_patterns);
+	return 0;
+
+pattern_name_free:
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+snk_free:
+	free(snk_nodes);
+src_free:
+	free(src_nodes);
+pattern_free:
+	free(node_patterns);
+data_free:
+	free(graph_data->node_data);
+memzone_free:
+	rte_memzone_free(mz);
+	return -ENOMEM;
+}
+
+/* Worker thread function */
+static int
+_graph_perf_wrapper(void *args)
+{
+	struct graph_lcore_data *data = args;
+	struct rte_graph *graph;
+
+	/* Lookup graph */
+	graph = rte_graph_lookup(rte_graph_id_to_name(data->graph_id));
+
+	/* Graph walk until done */
+	while (!data->done)
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
+static int
+measure_perf_get(rte_graph_t graph_id)
+{
+	const char *pattern = rte_graph_id_to_name(graph_id);
+	uint32_t lcore_id = rte_get_next_lcore(-1, 1, 0);
+	struct rte_graph_cluster_stats_param param;
+	struct rte_graph_cluster_stats *stats;
+	struct graph_lcore_data *data;
+
+	data = rte_zmalloc("Graph_perf", sizeof(struct graph_lcore_data),
+			   RTE_CACHE_LINE_SIZE);
+	data->graph_id = graph_id;
+	data->done = 0;
+
+	/* Run graph worker thread function */
+	rte_eal_remote_launch(_graph_perf_wrapper, data, lcore_id);
+
+	/* Collect stats for few msecs */
+	if (rte_graph_has_stats_feature()) {
+		memset(&param, 0, sizeof(param));
+		param.f = stdout;
+		param.socket_id = SOCKET_ID_ANY;
+		param.graph_patterns = &pattern;
+		param.nb_graph_patterns = 1;
+
+		stats = rte_graph_cluster_stats_create(&param);
+		if (stats == NULL) {
+			printf("Failed to create stats\n");
+			return -ENOMEM;
+		}
+
+		rte_delay_ms(3E2);
+		rte_graph_cluster_stats_get(stats, true);
+		rte_delay_ms(1E3);
+		rte_graph_cluster_stats_get(stats, false);
+		rte_graph_cluster_stats_destroy(stats);
+	} else
+		rte_delay_ms(1E3);
+
+	data->done = 1;
+	rte_eal_wait_lcore(lcore_id);
+
+	return 0;
+}
+
+static inline void
+graph_fini(void)
+{
+	const struct rte_memzone *mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	struct test_graph_perf *graph_data;
+
+	if (mz == NULL)
+		return;
+	graph_data = mz->addr;
+
+	rte_graph_destroy(rte_graph_id_to_name(graph_data->graph_id));
+	free(graph_data->node_data);
+	rte_memzone_free(rte_memzone_lookup(TEST_GRAPH_PERF_MZ));
+}
+
+static int
+measure_perf(void)
+{
+	const struct rte_memzone *mz;
+	struct test_graph_perf *graph_data;
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+
+	return measure_perf_get(graph_data->graph_id);
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk_brst_one(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_2src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_2snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_tree_4s_4n_1src_4snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_reverse_tree_3s_4n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_parallel_tree_5s_4n_4src_4snk(void)
+{
+	return measure_perf();
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr_brst_one(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 1);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			2
+ * sink:		1
+ */
+static inline int
+graph_init_hr_multi_src(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = {
+		{100}, {100}
+	};
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		2
+ */
+static inline int
+graph_init_hr_multi_snk(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][2] = { {50, 50} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		4
+ * src:			1
+ * sink:		4
+ */
+static inline int
+graph_init_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 0, 0, 0},
+			{50, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{33, 33, 0, 0},
+			{34, 34, 0, 0},
+			{33, 33, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0}
+		}
+	};
+	uint8_t src_map[][4] = { {100, 0, 0, 0} };
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		3
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_reverse_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25}
+		},
+		{
+			{33, 33, 33, 33},
+			{33, 33, 33, 33},
+			{34, 34, 34, 34},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 50, 50, 0},
+			{50, 50, 50, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+	};
+	uint8_t src_map[][4] = { {25, 25, 25, 25} };
+	uint8_t snk_map[][1] = { {100}, {100}, {0}, {0} };
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		5
+ * src:			4
+ * sink:		4
+ */
+static inline int
+graph_init_parallel_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+	};
+	uint8_t src_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_parallel", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/** Graph Creation cheat sheet
+ *  edge_map -> dictates graph flow from worker stage 0 to worker stage n-1.
+ *  src_map  -> dictates source nodes enqueue percentage to worker stage 0.
+ *  snk_map  -> dictates stage n-1 enqueue percentage to sink.
+ *
+ *  Layout:
+ *  edge_map[<nb_stages>][<nodes_per_stg>][<nodes_in_nxt_stg = nodes_per_stg>]
+ *  src_map[<nb_sources>][<nodes_in_stage0 = nodes_per_stage>]
+ *  snk_map[<nodes_in_stage(n-1) = nodes_per_stage>][<nb_sinks>]
+ *
+ *  The last array dictates the percentage of received objs to enqueue to next
+ *  stage.
+ *
+ *  Note: edge_map[][0][] will always be unused as it will receive from source
+ *
+ *  Example:
+ *	Graph:
+ *	http://bit.ly/2PqbqOy
+ *	Each stage(n) connects to all nodes in the next stage in decreasing
+ *	order.
+ *	Since we can't resize the edge_map dynamically we get away by creating
+ *	dummy nodes and assigning 0 percentages.
+ *	Max nodes across all stages = 4
+ *	stages = 3
+ *	nb_src = 1
+ *	nb_snk = 1
+ *			   // Stages
+ *	edge_map[][4][4] = {
+ *		// Nodes per stage
+ *		{
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25}
+ *		},	// This will be unused.
+ *		{
+ *		    // Nodes enabled in current stage + prev stage enq %
+ *		    {33, 33, 33, 33},
+ *		    {33, 33, 33, 33},
+ *		    {34, 34, 34, 34},
+ *		    {0, 0, 0, 0}
+ *		},
+ *		{
+ *		    {50, 50, 50, 0},
+ *		    {50, 50, 50, 0},
+ *		    {0, 0, 0, 0},
+ *		    {0, 0, 0, 0}
+ *		},
+ *	};
+ *	Above, each stage tells how much it should receive from previous except
+ *	from stage_0.
+ *
+ *	src_map[][4] = { {25, 25, 25, 25} };
+ *	Here, we tell each source the % it has to send to stage_0 nodes. In
+ *	case we want 2 source node we can declare as
+ *	src_map[][4] = { {25, 25, 25, 25}, {25, 25, 25, 25} };
+ *
+ *	snk_map[][1] = { {100}, {100}, {0}, {0} }
+ *	Here, we tell stage - 1 nodes how much to enqueue to sink_0.
+ *	If we have 2 sinks we can do as follows
+ *	snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
+ */
+
+static struct unit_test_suite graph_perf_testsuite = {
+	.suite_name = "Graph library performance test suite",
+	.setup = graph_perf_setup,
+	.teardown = graph_perf_teardown,
+	.unit_test_cases = {
+		TEST_CASE_ST(graph_init_hr, graph_fini,
+			     graph_hr_4s_1n_1src_1snk),
+		TEST_CASE_ST(graph_init_hr_brst_one, graph_fini,
+			     graph_hr_4s_1n_1src_1snk_brst_one),
+		TEST_CASE_ST(graph_init_hr_multi_src, graph_fini,
+			     graph_hr_4s_1n_2src_1snk),
+		TEST_CASE_ST(graph_init_hr_multi_snk, graph_fini,
+			     graph_hr_4s_1n_1src_2snk),
+		TEST_CASE_ST(graph_init_tree, graph_fini,
+			     graph_tree_4s_4n_1src_4snk),
+		TEST_CASE_ST(graph_init_reverse_tree, graph_fini,
+			     graph_reverse_tree_3s_4n_1src_1snk),
+		TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
+			     graph_parallel_tree_5s_4n_4src_4snk),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+test_graph_perf_func(void)
+{
+	return unit_test_suite_runner(&graph_perf_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_perf_autotest, test_graph_perf_func);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 15/26] node: add log infra and null node
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (13 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 14/26] graph: add performance testcase jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 16/26] node: add ethdev Rx node jerinj
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic,
	Nithin Dabilpuram, Pavan Nikhilesh, Bruce Richardson
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add log infra for node specific logging.
Also, add null rte_node that just ignores all the objects
directed to it.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 MAINTAINERS                          |  5 +++++
 config/common_base                   |  5 +++++
 doc/api/doxy-api.conf.in             |  1 +
 lib/Makefile                         |  3 +++
 lib/librte_node/Makefile             | 20 ++++++++++++++++++++
 lib/librte_node/log.c                | 14 ++++++++++++++
 lib/librte_node/meson.build          |  6 ++++++
 lib/librte_node/node_private.h       | 22 ++++++++++++++++++++++
 lib/librte_node/null.c               | 23 +++++++++++++++++++++++
 lib/librte_node/rte_node_version.map |  6 ++++++
 lib/meson.build                      |  5 ++++-
 meson.build                          |  1 +
 mk/rte.app.mk                        |  1 +
 13 files changed, 111 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/rte_node_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 32d0ea032..3959ed19a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1473,6 +1473,11 @@ M: Jerin Jacob <jerinj@marvell.com>
 M: Kiran Kumar K <kirankumark@marvell.com>
 F: lib/librte_graph/
 
+Nodes - EXPERIMENTAL
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+M: Pavan Nikhilesh <pbhagavatula@marvell.com>
+F: lib/librte_node/
+
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index 04a96aef5..442949ff1 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1082,6 +1082,11 @@ CONFIG_RTE_LIBRTE_GRAPH=y
 CONFIG_RTE_GRAPH_BURST_SIZE=256
 CONFIG_RTE_LIBRTE_GRAPH_STATS=y
 
+#
+# Compile librte_node
+#
+CONFIG_RTE_LIBRTE_NODE=y
+
 #
 # Compile the test application
 #
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 759a7213e..1d4f1a37d 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -49,6 +49,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_mempool \
                           @TOPDIR@/lib/librte_meter \
                           @TOPDIR@/lib/librte_metrics \
+                          @TOPDIR@/lib/librte_node \
                           @TOPDIR@/lib/librte_net \
                           @TOPDIR@/lib/librte_pci \
                           @TOPDIR@/lib/librte_pdump \
diff --git a/lib/Makefile b/lib/Makefile
index 1f572b659..50d61a338 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -122,6 +122,9 @@ DEPDIRS-librte_rcu := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
 DEPDIRS-librte_graph := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_NODE) += librte_node
+DEPDIRS-librte_node := librte_graph librte_lpm librte_ethdev librte_mbuf
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
new file mode 100644
index 000000000..5f9be6c2e
--- /dev/null
+++ b/lib/librte_node/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_node.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal -lrte_graph
+
+EXPORT_MAP := rte_node_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/log.c b/lib/librte_node/log.c
new file mode 100644
index 000000000..f035f91e8
--- /dev/null
+++ b/lib/librte_node/log.c
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "node_private.h"
+
+int rte_node_logtype;
+
+RTE_INIT(rte_node_init_log)
+{
+	rte_node_logtype = rte_log_register("lib.node");
+	if (rte_node_logtype >= 0)
+		rte_log_set_level(rte_node_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
new file mode 100644
index 000000000..655970614
--- /dev/null
+++ b/lib/librte_node/meson.build
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+sources = files('null.c', 'log.c')
+allow_experimental_apis = true
+deps += ['graph']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
new file mode 100644
index 000000000..f30902a94
--- /dev/null
+++ b/lib/librte_node/node_private.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __NODE_PRIVATE_H__
+#define __NODE_PRIVATE_H__
+
+#include <rte_common.h>
+#include <rte_log.h>
+
+extern int rte_node_logtype;
+#define NODE_LOG(level, node_name, ...)                                        \
+	rte_log(RTE_LOG_##level, rte_node_logtype,                             \
+		RTE_FMT("NODE %s: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",  \
+			node_name, __func__, __LINE__,                         \
+			RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define node_err(node_name, ...) NODE_LOG(ERR, node_name, __VA_ARGS__)
+#define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
+#define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
+
+#endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/null.c b/lib/librte_node/null.c
new file mode 100644
index 000000000..c7cd8b6df
--- /dev/null
+++ b/lib/librte_node/null.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_graph.h>
+
+static uint16_t
+null(struct rte_graph *graph, struct rte_node *node, void **objs,
+	uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(graph);
+
+	return nb_objs;
+}
+
+static struct rte_node_register null_node = {
+	.name = "null",
+	.process = null,
+};
+
+RTE_NODE_REGISTER(null_node);
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
new file mode 100644
index 000000000..f87163bb9
--- /dev/null
+++ b/lib/librte_node/rte_node_version.map
@@ -0,0 +1,6 @@
+EXPERIMENTAL {
+	global:
+
+	rte_node_logtype;
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index c43d86bb9..147129b0b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'graph', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'node', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
@@ -186,6 +186,9 @@ foreach l:libraries
 
 			dpdk_libraries = [shared_lib] + dpdk_libraries
 			dpdk_static_libraries = [static_lib] + dpdk_static_libraries
+			if libname == 'rte_node'
+				dpdk_graph_nodes = [static_lib]
+			endif
 		endif # sources.length() > 0
 
 		set_variable('shared_rte_' + name, shared_dep)
diff --git a/meson.build b/meson.build
index b7ae9c8d9..811c96421 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@ cc = meson.get_compiler('c')
 dpdk_conf = configuration_data()
 dpdk_libraries = []
 dpdk_static_libraries = []
+dpdk_graph_nodes = []
 dpdk_driver_classes = []
 dpdk_drivers = []
 dpdk_extra_ldflags = []
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index b1195f09a..68d7806a4 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -99,6 +99,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
 _LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
+_LDLIBS-$(CONFIG_RTE_LIBRTE_NODE)           += -lrte_node
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 16/26] node: add ethdev Rx node
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (14 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 15/26] node: add log infra and null node jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 17/26] node: add ethdev Tx node jerinj
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add source rte_node ethdev_rx process function and register
it. This node is a source node that will be called periodically
and when called, performs rte_eth_rx_burst() on a specific
(port, queue) pair and enqueue them as stream of objects to
next node.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |   3 +-
 lib/librte_node/ethdev_rx.c      | 221 +++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_rx_priv.h |  81 +++++++++++
 lib/librte_node/meson.build      |   4 +-
 4 files changed, 306 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 5f9be6c2e..5a5f589ba 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -9,12 +9,13 @@ LIB = librte_node.a
 
 CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
-LDLIBS += -lrte_eal -lrte_graph
+LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_rx.c b/lib/librte_node/ethdev_rx.c
new file mode 100644
index 000000000..5cc736598
--- /dev/null
+++ b/lib/librte_node/ethdev_rx.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_rx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_rx_node_main ethdev_rx_main;
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process_inline(struct rte_graph *graph, struct rte_node *node,
+			      uint16_t port, uint16_t queue)
+{
+	uint16_t count, next_index = ETHDEV_RX_NEXT_IP4_LOOKUP;
+
+	/* Get pkts from port */
+	count = rte_eth_rx_burst(port, queue, (struct rte_mbuf **)node->objs,
+				 RTE_GRAPH_BURST_SIZE);
+
+	if (!count)
+		return 0;
+	node->idx = count;
+	/* Enqueue to next node */
+	rte_node_next_stream_move(graph, node, next_index);
+
+	return count;
+}
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t cnt)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	uint16_t n_pkts = 0;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(cnt);
+
+	n_pkts = ethdev_rx_node_process_inline(graph, node, ctx->port_id,
+					       ctx->queue_id);
+	return n_pkts;
+}
+
+static inline uint32_t
+l3_ptype(uint16_t etype, uint32_t ptype)
+{
+	ptype = ptype & ~RTE_PTYPE_L3_MASK;
+	if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
+		ptype |= RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
+	else if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6))
+		ptype |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	return ptype;
+}
+
+/* Callback for soft ptype parsing */
+static uint16_t
+eth_pkt_parse_cb(uint16_t port, uint16_t queue, struct rte_mbuf **mbufs,
+		 uint16_t nb_pkts, uint16_t max_pkts, void *user_param)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3;
+	struct rte_ether_hdr *eth_hdr;
+	uint16_t etype, n_left;
+	struct rte_mbuf **pkts;
+
+	RTE_SET_USED(port);
+	RTE_SET_USED(queue);
+	RTE_SET_USED(max_pkts);
+	RTE_SET_USED(user_param);
+
+	pkts = mbufs;
+	n_left = nb_pkts;
+	while (n_left >= 12) {
+
+		/* Prefetch next-next mbufs */
+		rte_prefetch0(pkts[8]);
+		rte_prefetch0(pkts[9]);
+		rte_prefetch0(pkts[10]);
+		rte_prefetch0(pkts[11]);
+
+		/* Prefetch next mbuf data */
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[4], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[5], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[6], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[7], struct rte_ether_hdr *));
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+		pkts += 4;
+		n_left -= 4;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf1 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf1->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf2 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf2->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf3 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf3->packet_type = l3_ptype(etype, 0);
+	}
+
+	while (n_left > 0) {
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left -= 1;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+	}
+
+	return nb_pkts;
+}
+
+#define MAX_PTYPES 16
+static int
+ethdev_ptype_setup(uint16_t port, uint16_t queue)
+{
+	uint8_t l3_ipv4 = 0, l3_ipv6 = 0;
+	uint32_t ptypes[MAX_PTYPES];
+	int i, rc;
+
+	/* Check IPv4 & IPv6 ptype support */
+	rc = rte_eth_dev_get_supported_ptypes(port, RTE_PTYPE_L3_MASK, ptypes,
+					      MAX_PTYPES);
+	for (i = 0; i < rc; i++) {
+		if (ptypes[i] & RTE_PTYPE_L3_IPV4)
+			l3_ipv4 = 1;
+		if (ptypes[i] & RTE_PTYPE_L3_IPV6)
+			l3_ipv6 = 1;
+	}
+
+	if (!l3_ipv4 || !l3_ipv6) {
+		node_info("ethdev_rx",
+			  "Enabling ptype callback for required ptypes on port %u\n",
+			  port);
+
+		if (!rte_eth_add_rx_callback(port, queue, eth_pkt_parse_cb,
+					     NULL)) {
+			node_err("ethdev_rx",
+				 "Failed to add rx ptype cb: port=%d, queue=%d\n",
+				 port, queue);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int
+ethdev_rx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	ethdev_rx_node_elem_t *elem = ethdev_rx_main.head;
+
+	RTE_SET_USED(graph);
+
+	while (elem) {
+		if (elem->nid == node->id) {
+			/* Update node specific context */
+			memcpy(ctx, &elem->ctx, sizeof(ethdev_rx_node_ctx_t));
+			break;
+		}
+		elem = elem->next;
+	}
+
+	RTE_VERIFY(elem != NULL);
+
+	/* Check and setup ptype */
+	return ethdev_ptype_setup(ctx->port_id, ctx->queue_id);
+}
+
+struct ethdev_rx_node_main *
+ethdev_rx_get_node_data_get(void)
+{
+	return &ethdev_rx_main;
+}
+
+static struct rte_node_register ethdev_rx_node_base = {
+	.process = ethdev_rx_node_process,
+	.flags = RTE_NODE_SOURCE_F,
+	.name = "ethdev_rx",
+
+	.init = ethdev_rx_node_init,
+
+	.nb_edges = ETHDEV_RX_NEXT_MAX,
+	.next_nodes = {[ETHDEV_RX_NEXT_IP4_LOOKUP] = "ip4_lookup"},
+};
+
+struct rte_node_register *
+ethdev_rx_node_get(void)
+{
+	return &ethdev_rx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_rx_node_base);
diff --git a/lib/librte_node/ethdev_rx_priv.h b/lib/librte_node/ethdev_rx_priv.h
new file mode 100644
index 000000000..2d7195a36
--- /dev/null
+++ b/lib/librte_node/ethdev_rx_priv.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_RX_PRIV_H__
+#define __INCLUDE_ETHDEV_RX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+struct ethdev_rx_node_elem;
+struct ethdev_rx_node_ctx;
+typedef struct ethdev_rx_node_elem ethdev_rx_node_elem_t;
+typedef struct ethdev_rx_node_ctx ethdev_rx_node_ctx_t;
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node context structure.
+ */
+struct ethdev_rx_node_ctx {
+	uint16_t port_id;  /**< Port identifier of the Rx node. */
+	uint16_t queue_id; /**< Queue identifier of the Rx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node list element structure.
+ */
+struct ethdev_rx_node_elem {
+	struct ethdev_rx_node_elem *next;
+	/**< Pointer to the next Rx node element. */
+	struct ethdev_rx_node_ctx ctx;
+	/**< Rx node context. */
+	rte_node_t nid;
+	/**< Node identifier of the Rx node. */
+};
+
+enum ethdev_rx_next_nodes {
+	ETHDEV_RX_NEXT_IP4_LOOKUP,
+	ETHDEV_RX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Rx node main structure.
+ */
+struct ethdev_rx_node_main {
+	ethdev_rx_node_elem_t *head;
+	/**< Pointer to the head Rx node element. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Rx node data.
+ */
+struct ethdev_rx_node_main *ethdev_rx_get_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Rx node.
+ */
+struct rte_node_register *ethdev_rx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_RX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 655970614..4adfd6580 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c')
 allow_experimental_apis = true
-deps += ['graph']
+deps += ['graph', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 17/26] node: add ethdev Tx node
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (15 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 16/26] node: add ethdev Rx node jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 18/26] node: add ethdev Rx and Tx node ctrl API jerinj
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add rte_node ethdev_tx process function and register it to
graph infra. This node has a specific (port, tx-queue) as context
and it enqueue's all the packets received to that specific queue pair.
When rte_eth_tx_burst() i.e enqueue to queue pair fails, packets
are forwarded to pkt_drop node to be free'd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |  3 +-
 lib/librte_node/ethdev_tx.c      | 86 ++++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_tx_priv.h | 62 +++++++++++++++++++++++
 lib/librte_node/meson.build      |  4 +-
 4 files changed, 152 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 5a5f589ba..14a293982 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -9,7 +9,7 @@ LIB = librte_node.a
 
 CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
-LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
@@ -17,5 +17,6 @@ EXPORT_MAP := rte_node_version.map
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_tx.c b/lib/librte_node/ethdev_tx.c
new file mode 100644
index 000000000..075149089
--- /dev/null
+++ b/lib/librte_node/ethdev_tx.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_tx_priv.h"
+
+static struct ethdev_tx_node_main ethdev_tx_main;
+
+static uint16_t
+ethdev_tx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t nb_objs)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint16_t port, queue;
+	uint16_t count;
+
+	/* Get Tx port id */
+	port = ctx->port;
+	queue = ctx->queue;
+
+	count = rte_eth_tx_burst(port, queue, (struct rte_mbuf **)objs,
+				 nb_objs);
+
+	/* Redirect unsent pkts to drop node */
+	if (count != nb_objs) {
+		rte_node_enqueue(graph, node, ETHDEV_TX_NEXT_PKT_DROP,
+				 &objs[count], nb_objs - count);
+	}
+
+	return count;
+}
+
+static int
+ethdev_tx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint64_t port_id = RTE_MAX_ETHPORTS;
+	int i;
+
+	/* Find our port id */
+	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+		if (ethdev_tx_main.nodes[i] == node->id) {
+			port_id = i;
+			break;
+		}
+	}
+	RTE_VERIFY(port_id < RTE_MAX_ETHPORTS);
+
+	/* Update port and queue */
+	ctx->port = port_id;
+	ctx->queue = graph->id;
+
+	return 0;
+}
+
+struct ethdev_tx_node_main *
+ethdev_tx_node_data_get(void)
+{
+	return &ethdev_tx_main;
+}
+
+static struct rte_node_register ethdev_tx_node_base = {
+	.process = ethdev_tx_node_process,
+	.name = "ethdev_tx",
+
+	.init = ethdev_tx_node_init,
+
+	.nb_edges = ETHDEV_TX_NEXT_MAX,
+	.next_nodes = {
+		[ETHDEV_TX_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+struct rte_node_register *
+ethdev_tx_node_get(void)
+{
+	return &ethdev_tx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_tx_node_base);
diff --git a/lib/librte_node/ethdev_tx_priv.h b/lib/librte_node/ethdev_tx_priv.h
new file mode 100644
index 000000000..586bff44a
--- /dev/null
+++ b/lib/librte_node/ethdev_tx_priv.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_TX_PRIV_H__
+#define __INCLUDE_ETHDEV_TX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ethdev_tx_node_ctx;
+typedef struct ethdev_tx_node_ctx ethdev_tx_node_ctx_t;
+
+enum ethdev_tx_next_nodes {
+	ETHDEV_TX_NEXT_PKT_DROP,
+	ETHDEV_TX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node context structure.
+ */
+struct ethdev_tx_node_ctx {
+	uint16_t port;	/**< Port identifier of the Ethernet Tx node. */
+	uint16_t queue; /**< Queue identifier of the Ethernet Tx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node main structure.
+ */
+struct ethdev_tx_node_main {
+	uint32_t nodes[RTE_MAX_ETHPORTS]; /**< Tx nodes for each ethdev port. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Tx node data.
+ */
+struct ethdev_tx_node_main *ethdev_tx_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Tx node.
+ */
+struct rte_node_register *ethdev_tx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_TX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 4adfd6580..c3e35276b 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
 allow_experimental_apis = true
-deps += ['graph', 'ethdev']
+deps += ['graph', 'mbuf', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 18/26] node: add ethdev Rx and Tx node ctrl API
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (16 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 17/26] node: add ethdev Tx node jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 19/26] node: ipv4 lookup for arm64 jerinj
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ctrl api to setup ethdev_rx and ethdev_tx node.
This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
nodes with specific (port, queue) pairs updated in their context.
All the ethdev ports and queues are setup before this api
is called.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 doc/api/doxy-api-index.md            |  2 +
 lib/librte_node/Makefile             |  6 +-
 lib/librte_node/ethdev_ctrl.c        | 99 ++++++++++++++++++++++++++++
 lib/librte_node/meson.build          |  5 +-
 lib/librte_node/node_private.h       | 74 +++++++++++++++++++++
 lib/librte_node/rte_node_eth_api.h   | 70 ++++++++++++++++++++
 lib/librte_node/rte_node_version.map |  1 +
 7 files changed, 254 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index fd2ff64d7..1784674df 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -161,6 +161,8 @@ The public API headers are grouped by topics:
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
     [graph_worker]     (@ref rte_graph_worker.h)
+  * graph_nodes:
+    [eth_node]         (@ref rte_node_eth_api.h),
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 14a293982..c7b291c3c 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -9,7 +9,7 @@ LIB = librte_node.a
 
 CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -18,5 +18,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
new file mode 100644
index 000000000..971cf0fb8
--- /dev/null
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_node_eth_api.h>
+
+#include "ethdev_rx_priv.h"
+#include "ethdev_tx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_ctrl {
+	uint16_t nb_graphs;
+} ctrl;
+
+int
+rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
+		    uint16_t nb_graphs)
+{
+	struct ethdev_tx_node_main *tx_node_data;
+	uint16_t tx_q_used, rx_q_used, port_id;
+	struct rte_node_register *tx_node;
+	char name[RTE_NODE_NAMESIZE];
+	struct rte_mempool *mp;
+	uint32_t id;
+	int i, j;
+
+	tx_node_data = ethdev_tx_node_data_get();
+	tx_node = ethdev_tx_node_get();
+	for (i = 0; i < nb_confs; i++) {
+		port_id = conf[i].port_id;
+
+		if (!rte_eth_dev_is_valid_port(port_id))
+			return -EINVAL;
+
+		/* Check for mbuf minimum private size requirement */
+		for (j = 0; j < conf[i].mp_count; j++) {
+			mp = conf[i].mp[j];
+			if (!mp)
+				continue;
+			/* Check for minimum private space */
+			if (rte_pktmbuf_priv_size(mp) <
+			    RTE_NODE_MBUF_PRIV2_SIZE) {
+				node_err("ethdev",
+					 "Minimum mbuf priv size requirement not met by mp %s",
+					 mp->name);
+				return -EINVAL;
+			}
+		}
+
+		rx_q_used = conf[i].num_rx_queues;
+		tx_q_used = conf[i].num_tx_queues;
+		/* Check if we have a txq for each worker */
+		if (tx_q_used < nb_graphs)
+			return -EINVAL;
+
+		/* Create node for each rx port queue pair */
+		for (j = 0; j < rx_q_used; j++) {
+			struct ethdev_rx_node_main *rx_node_data;
+			struct rte_node_register *rx_node;
+			ethdev_rx_node_elem_t *elem;
+
+			rx_node_data = ethdev_rx_get_node_data_get();
+			rx_node = ethdev_rx_node_get();
+			snprintf(name, sizeof(name), "%u-%u", port_id, j);
+			/* Clone a new rx node with same edges as parent */
+			id = rte_node_clone(rx_node->id, name);
+			if (id == RTE_NODE_ID_INVALID)
+				return -EIO;
+
+			/* Add it to list of ethdev rx nodes for lookup */
+			elem = malloc(sizeof(ethdev_rx_node_elem_t));
+			memset(elem, 0, sizeof(ethdev_rx_node_elem_t));
+			elem->ctx.port_id = port_id;
+			elem->ctx.queue_id = j;
+			elem->nid = id;
+			elem->next = rx_node_data->head;
+			rx_node_data->head = elem;
+
+			node_dbg("ethdev", "Rx node %s-%s: is at %u",
+				 rx_node->name, name, id);
+		}
+
+		/* Create a per port tx node from base node */
+		snprintf(name, sizeof(name), "%u", port_id);
+		/* Clone a new node with same edges as parent */
+		id = rte_node_clone(tx_node->id, name);
+		tx_node_data->nodes[port_id] = id;
+
+		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
+			 name, id);
+	}
+
+	ctrl.nb_graphs = nb_graphs;
+	return 0;
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index c3e35276b..f34162aa7 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
+headers = files('rte_node_eth_api.h')
 allow_experimental_apis = true
-deps += ['graph', 'mbuf', 'ethdev']
+deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
index f30902a94..141448546 100644
--- a/lib/librte_node/node_private.h
+++ b/lib/librte_node/node_private.h
@@ -6,7 +6,9 @@
 #define __NODE_PRIVATE_H__
 
 #include <rte_common.h>
+#include <rte_crypto.h>
 #include <rte_log.h>
+#include <rte_mbuf.h>
 
 extern int rte_node_logtype;
 #define NODE_LOG(level, node_name, ...)                                        \
@@ -19,4 +21,76 @@ extern int rte_node_logtype;
 #define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
 #define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store next hop, ttl and checksum.
+ */
+struct rte_node_mbuf_priv1 {
+	union {
+		/* IP4 rewrite */
+		struct {
+			uint16_t nh;
+			uint16_t ttl;
+			uint32_t cksum;
+		};
+
+		uint64_t u;
+	};
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store crypto operation.
+ */
+struct rte_node_mbuf_priv2 {
+	union {
+		/* Sym crypto */
+		struct {
+			struct rte_crypto_op op;
+		};
+	};
+} __rte_cache_aligned;
+
+#define RTE_NODE_MBUF_PRIV2_SIZE sizeof(struct rte_node_mbuf_priv2)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv1 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv1.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv1 *
+rte_node_mbuf_priv1(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv1 *)&m->udata64;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv2 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv2.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv2 *
+rte_node_mbuf_priv2(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv2 *)rte_mbuf_to_priv(m);
+}
+
 #endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/rte_node_eth_api.h b/lib/librte_node/rte_node_eth_api.h
new file mode 100644
index 000000000..39b31b45b
--- /dev/null
+++ b/lib/librte_node/rte_node_eth_api.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_ETH_API_H__
+#define __INCLUDE_RTE_NODE_ETH_API_H__
+
+/**
+ * @file rte_node_eth_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to setup ethdev_rx and ethdev_tx nodes
+ * and its queue associations.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_mempool.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Port config for ethdev_rx and ethdev_tx node.
+ */
+struct rte_node_ethdev_config {
+	uint16_t port_id;
+	/**< Port identifier */
+	uint16_t num_rx_queues;
+	/**< Number of Rx queues. */
+	uint16_t num_tx_queues;
+	/**< Number of Tx queues. */
+	struct rte_mempool **mp;
+	/**< Array of mempools associated to Rx queue. */
+	uint16_t mp_count;
+	/**< Size of mp array. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Initializes ethdev nodes.
+ *
+ * @param cfg
+ *   Array of ethdev config that identifies which port's
+ *   ethdev_rx and ethdev_tx nodes need to be created
+ *   and queue association.
+ * @param cnt
+ *   Size of cfg array.
+ * @param nb_graphs
+ *   Number of graphs that will be used.
+ *
+ * @return
+ *   0 on successful initialization, negative otherwise.
+ */
+__rte_experimental
+int rte_node_eth_config(struct rte_node_ethdev_config *cfg,
+			uint16_t cnt, uint16_t nb_graphs);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_ETH_API_H__ */
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index f87163bb9..c6c71bd02 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -1,6 +1,7 @@
 EXPERIMENTAL {
 	global:
 
+	rte_node_eth_config;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 19/26] node: ipv4 lookup for arm64
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (17 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 18/26] node: add ethdev Rx and Tx node ctrl API jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup for x86 jerinj
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add arm64 specific IPv4 lookup process function
for ip4_lookup node. This node performs LPM lookup
on every packet received and forwards it to a next
node that is identified by lookup result.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 doc/api/doxy-api-index.md          |   1 +
 lib/librte_node/Makefile           |   4 +-
 lib/librte_node/ip4_lookup.c       | 306 +++++++++++++++++++++++++++++
 lib/librte_node/meson.build        |   5 +-
 lib/librte_node/rte_node_ip4_api.h |  43 ++++
 5 files changed, 356 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/rte_node_ip4_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 1784674df..3908fddde 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -163,6 +163,7 @@ The public API headers are grouped by topics:
     [graph_worker]     (@ref rte_graph_worker.h)
   * graph_nodes:
     [eth_node]         (@ref rte_node_eth_api.h),
+    [ip4_node]         (@ref rte_node_ip4_api.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index c7b291c3c..d85a9e9eb 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -9,7 +9,7 @@ LIB = librte_node.a
 
 CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_lpm -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -19,8 +19,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 
 # install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
new file mode 100644
index 000000000..d7fcd1158
--- /dev/null
+++ b/lib/librte_node/ip4_lookup.c
@@ -0,0 +1,306 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_lpm.h>
+#include <rte_mbuf.h>
+#include <rte_node_ip4_api.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+
+#include "node_private.h"
+
+#define IPV4_L3FWD_LPM_MAX_RULES 1024
+#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
+
+/* IP4 Lookup global data struct */
+struct ip4_lookup_node_main {
+	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
+};
+
+#if defined(RTE_MACHINE_CPUFLAG_NEON)
+/* ARM64 NEON */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	struct rte_ether_hdr *eth_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	rte_edge_t next_index;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t result;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int32x4_t dip;
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+#define OBJS_PER_CLINE (RTE_CACHE_LINE_SIZE / sizeof(void *))
+	for (i = OBJS_PER_CLINE; i < RTE_GRAPH_BURST_SIZE; i += OBJS_PER_CLINE)
+		rte_prefetch0(&objs[i]);
+
+	for (i = 0; i < 4 && i < n_left_from; i++) {
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[i], struct rte_ether_hdr *) + 1);
+	}
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+#if RTE_GRAPH_BURST_SIZE > 64
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from >= 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+#endif
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from >= 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
+						       struct rte_ether_hdr *) +
+				      1);
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
+						       struct rte_ether_hdr *) +
+				      1);
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
+						       struct rte_ether_hdr *) +
+				      1);
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
+						       struct rte_ether_hdr *) +
+				      1);
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 0);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[1] = ipv4_hdr->time_to_live;
+		priv01.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf1 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 1);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[5] = ipv4_hdr->time_to_live;
+		priv01.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf2 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 2);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[1] = ipv4_hdr->time_to_live;
+		priv23.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf3 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 3);
+
+		dip = vreinterpretq_s32_u8(
+			vrev32q_u8(vreinterpretq_u8_s32(dip)));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[5] = ipv4_hdr->time_to_live;
+		priv23.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, result.u32, drop_nh);
+		priv01.u16[0] = result.u16[0];
+		priv01.u16[4] = result.u16[2];
+		priv23.u16[0] = result.u16[4];
+		priv23.u16[4] = result.u16[6];
+
+		rte_node_mbuf_priv1(mbuf0)->u = priv01.u64[0];
+		rte_node_mbuf_priv1(mbuf1)->u = priv01.u64[1];
+		rte_node_mbuf_priv1(mbuf2)->u = priv23.u64[0];
+		rte_node_mbuf_priv1(mbuf3)->u = priv23.u64[1];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec = ((next_index == result.u16[1]) &&
+				       (result.u16[1] == result.u16[3]) &&
+				       (result.u16[3] == result.u16[5]) &&
+				       (result.u16[5] == result.u16[7]));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == result.u16[1]) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[1],
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == result.u16[3]) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[3],
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == result.u16[5]) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[5],
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == result.u16[7]) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[7],
+						    from[3]);
+			}
+
+			from += 4;
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+		uint16_t next0;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next0 = (uint16_t)next_hop;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#else
+
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+	return nb_objs;
+}
+
+#endif
+
+static int
+ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+
+	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_lookup_node = {
+	.process = ip4_lookup_node_process,
+	.name = "ip4_lookup",
+
+	.init = ip4_lookup_node_init,
+
+	.nb_edges = RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	.next_nodes = {
+		[RTE_NODE_IP4_LOOKUP_NEXT_REWRITE] = "ip4_rewrite",
+		[RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(ip4_lookup_node);
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index f34162aa7..ddea1237a 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,7 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
-headers = files('rte_node_eth_api.h')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
+		'ethdev_ctrl.c')
+headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
new file mode 100644
index 000000000..37c12bf82
--- /dev/null
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_IP4_API_H__
+#define __INCLUDE_RTE_NODE_IP4_API_H__
+
+/**
+ * @file rte_node_ip4_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to do control path functions of ip4_* nodes
+ * like ip4_lookup, ip4_rewrite.
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * IP4 lookup next nodes.
+ */
+enum rte_node_ip4_lookup_next {
+	RTE_NODE_IP4_LOOKUP_NEXT_REWRITE,
+	/**< Rewrite node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP,
+	/**< Packet drop node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	/**< Number of next nodes of lookup node. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_IP4_API_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (18 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 19/26] node: ipv4 lookup for arm64 jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-19 12:25   ` Ray Kinsella
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 21/26] node: add ipv4 rewrite node jerinj
                   ` (6 subsequent siblings)
  26 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup
rte_node. This node performs LPM lookup using x86_64
vector supported RTE_LPM API on every packet received
and forwards it to a next node that is identified by
lookup result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ip4_lookup.c | 245 +++++++++++++++++++++++++++++++++++
 1 file changed, 245 insertions(+)

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index d7fcd1158..c003e9c91 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -264,6 +264,251 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 	return nb_objs;
 }
 
+#elif defined(RTE_ARCH_X86)
+
+/* X86 SSE */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	rte_edge_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	struct rte_ether_hdr *eth_hdr;
+	uint32_t ip0, ip1, ip2, ip3;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t dst;
+	__m128i dip; /* SSE register */
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	if (n_left_from >= 4) {
+		for (i = 0; i < 4; i++) {
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
+						       struct rte_ether_hdr *) +
+				      1);
+		}
+	}
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from >= 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from >= 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
+						       struct rte_ether_hdr *) +
+				      1);
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
+						       struct rte_ether_hdr *) +
+				      1);
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
+						       struct rte_ether_hdr *) +
+				      1);
+			rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
+						       struct rte_ether_hdr *) +
+				      1);
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		ip0 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf1 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		ip1 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf2 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		ip2 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf3 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		ip3 = ipv4_hdr->dst_addr;
+
+		/* Prepare for lookup x4 */
+		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
+
+		/* Byte swap 4 IPV4 addresses. */
+		const __m128i bswap_mask = _mm_set_epi8(
+			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
+		dip = _mm_shuffle_epi8(dip, bswap_mask);
+
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr->time_to_live;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
+
+		/* Extract next node id and NH */
+		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] & 0xFFFF;
+		next0 = (dst.u32[0] >> 16);
+
+		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] & 0xFFFF;
+		next1 = (dst.u32[1] >> 16);
+
+		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] & 0xFFFF;
+		next2 = (dst.u32[2] >> 16);
+
+		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] & 0xFFFF;
+		next3 = (dst.u32[3] >> 16);
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			(next_index ^ next0) | (next_index ^ next1) |
+			(next_index ^ next2) | (next_index ^ next3);
+
+		if (unlikely(fix_spec)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = next_hop & 0xFFFF;
+		next0 = (next_hop >> 16);
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	/* Copy things successfully speculated till now */
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
 #else
 
 static uint16_t
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 21/26] node: add ipv4 rewrite node
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (19 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup for x86 jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 22/26] node: add ipv4 rewrite and lookup ctrl API jerinj
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Kiran Kumar K <kirankumark@marvell.com>

Add ip4 rewrite process function for ip4_rewrite
rte_node. On every packet received by this node,
header is overwritten with new data before forwarding
it to next node. Header data to overwrite with is
identified by next hop id passed in mbuf priv data
by previous node.

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_node/Makefile           |   1 +
 lib/librte_node/ip4_rewrite.c      | 269 +++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h |  55 ++++++
 lib/librte_node/meson.build        |   2 +-
 4 files changed, 326 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index d85a9e9eb..be7e7e338 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -20,6 +20,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
new file mode 100644
index 000000000..f317f7f4f
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite.c
@@ -0,0 +1,269 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+#include <rte_node_ip4_api.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_vect.h>
+
+#include "ip4_rewrite_priv.h"
+#include "node_private.h"
+
+static struct ip4_rewrite_node_main *ip4_rewrite_nm;
+
+static uint16_t
+ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
+			 void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct ip4_rewrite_nh_header *nh = ip4_rewrite_nm->nh;
+	uint16_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ip0, *ip1, *ip2, *ip3;
+	uint16_t n_left_from, held = 0, last_spec = 0;
+	void *d0, *d1, *d2, *d3;
+	void **to_next, **from;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int i;
+
+	/* Speculative next as last next */
+	next_index = *(uint16_t *)node->ctx;
+	rte_prefetch0(nh);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(pkts[i]);
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	/* Update Ethernet header of pkts */
+	while (n_left_from >= 4) {
+		if (likely(n_left_from >= 7)) {
+			/* Prefetch only next-mbuf struct and priv area.
+			 * Data need not be prefetched as we only write.
+			 */
+			rte_prefetch0(pkts[4]);
+			rte_prefetch0(pkts[5]);
+			rte_prefetch0(pkts[6]);
+			rte_prefetch0(pkts[7]);
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+		priv01.u64[0] = rte_node_mbuf_priv1(mbuf0)->u;
+		priv01.u64[1] = rte_node_mbuf_priv1(mbuf1)->u;
+		priv23.u64[0] = rte_node_mbuf_priv1(mbuf2)->u;
+		priv23.u64[1] = rte_node_mbuf_priv1(mbuf3)->u;
+
+		/* Increment checksum by one. */
+		priv01.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv01.u32[3] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[3] += rte_cpu_to_be_16(0x0100);
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf0 */
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[priv01.u16[0]].rewrite_data,
+			   nh[priv01.u16[0]].rewrite_len);
+
+		next0 = nh[priv01.u16[0]].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		ip0->time_to_live = priv01.u16[1] - 1;
+		ip0->hdr_checksum = priv01.u16[2] + priv01.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf1 */
+		d1 = rte_pktmbuf_mtod(mbuf1, void *);
+		rte_memcpy(d1, nh[priv01.u16[4]].rewrite_data,
+			   nh[priv01.u16[4]].rewrite_len);
+
+		next1 = nh[priv01.u16[4]].tx_node;
+		ip1 = (struct rte_ipv4_hdr *)((uint8_t *)d1 +
+					      sizeof(struct rte_ether_hdr));
+		ip1->time_to_live = priv01.u16[5] - 1;
+		ip1->hdr_checksum = priv01.u16[6] + priv01.u16[7];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf2 */
+		d2 = rte_pktmbuf_mtod(mbuf2, void *);
+		rte_memcpy(d2, nh[priv23.u16[0]].rewrite_data,
+			   nh[priv23.u16[0]].rewrite_len);
+		next2 = nh[priv23.u16[0]].tx_node;
+		ip2 = (struct rte_ipv4_hdr *)((uint8_t *)d2 +
+					      sizeof(struct rte_ether_hdr));
+		ip2->time_to_live = priv23.u16[1] - 1;
+		ip2->hdr_checksum = priv23.u16[2] + priv23.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf3 */
+		d3 = rte_pktmbuf_mtod(mbuf3, void *);
+		rte_memcpy(d3, nh[priv23.u16[4]].rewrite_data,
+			   nh[priv23.u16[4]].rewrite_len);
+
+		next3 = nh[priv23.u16[4]].tx_node;
+		ip3 = (struct rte_ipv4_hdr *)((uint8_t *)d3 +
+					      sizeof(struct rte_ether_hdr));
+		ip3->time_to_live = priv23.u16[5] - 1;
+		ip3->hdr_checksum = priv23.u16[6] + priv23.u16[7];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			((next_index == next0) && (next0 == next1) &&
+			 (next1 == next2) && (next2 == next3));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+			/* Change speculation if last two are same */
+			if ((next_index != next3) && (next2 == next3)) {
+				/* Put the current speculated node */
+				rte_node_next_stream_put(graph, node,
+							 next_index, held);
+				held = 0;
+
+				/* Get next speculated stream */
+				next_index = next3;
+				to_next = rte_node_next_stream_get(
+					graph, node, next_index, nb_objs);
+			}
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint16_t chksum;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_data,
+			   nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_len);
+
+		next0 = nh[rte_node_mbuf_priv1(mbuf0)->nh].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		chksum = rte_node_mbuf_priv1(mbuf0)->cksum +
+			 rte_cpu_to_be_16(0x0100);
+		chksum += chksum >= 0xffff;
+		ip0->hdr_checksum = chksum;
+		ip0->time_to_live = rte_node_mbuf_priv1(mbuf0)->ttl - 1;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+	/* Save the last next used */
+	*(uint16_t *)node->ctx = next_index;
+
+	return nb_objs;
+}
+
+static int
+ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_rewrite_node = {
+	.process = ip4_rewrite_node_process,
+	.name = "ip4_rewrite",
+	/* Default edge i.e '0' is pkt drop */
+	.nb_edges = 1,
+	.next_nodes = {
+		[0] = "pkt_drop",
+	},
+	.init = ip4_rewrite_node_init,
+};
+
+RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
new file mode 100644
index 000000000..420996a03
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_IP4_REWRITE_PRIV_H__
+#define __INCLUDE_IP4_REWRITE_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+#define RTE_GRAPH_IP4_REWRITE_MAX_NH 64
+#define RTE_GRAPH_IP4_REWRITE_MAX_LEN 56
+
+/**
+ * @internal
+ *
+ * Ipv4 rewrite next hop header data structure. Used to store port specific
+ * rewrite data.
+ */
+struct ip4_rewrite_nh_header {
+	uint16_t rewrite_len; /**< Header rewrite length. */
+	uint16_t tx_node;     /**< Tx node next index identifier. */
+	uint16_t enabled;     /**< NH enable flag */
+	uint16_t rsvd;
+	union {
+		struct {
+			struct rte_ether_addr dst;
+			/**< Destination mac address. */
+			struct rte_ether_addr src;
+			/**< Source mac address. */
+		};
+		uint8_t rewrite_data[RTE_GRAPH_IP4_REWRITE_MAX_LEN];
+		/**< Generic rewrite data */
+	};
+};
+
+/**
+ * @internal
+ *
+ * Ipv4 node main data structure.
+ */
+struct ip4_rewrite_node_main {
+	struct ip4_rewrite_nh_header nh[RTE_GRAPH_IP4_REWRITE_MAX_NH];
+	/**< Array of next hop header data */
+	uint16_t next_index[RTE_MAX_ETHPORTS];
+	/**< Next index of each configured port. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_IP4_REWRITE_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index ddea1237a..ef056d9af 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 22/26] node: add ipv4 rewrite and lookup ctrl API
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (20 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 21/26] node: add ipv4 rewrite node jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 23/26] node: add pkt drop node jerinj
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ip4_rewrite and ip4_lookup ctrl API. ip4_lookup ctrl
API is used to add route entries for LPM lookup with
result data containing next hop id and next proto.
ip4_rewrite ctrl API is used to add rewrite data for
every next hop.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ethdev_ctrl.c        | 18 ++++++-
 lib/librte_node/ip4_lookup.c         | 80 ++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite.c        | 56 +++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h   | 22 ++++++++
 lib/librte_node/rte_node_ip4_api.h   | 44 +++++++++++++++
 lib/librte_node/rte_node_version.map |  2 +
 6 files changed, 221 insertions(+), 1 deletion(-)

diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
index 971cf0fb8..48ef8746f 100644
--- a/lib/librte_node/ethdev_ctrl.c
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -10,6 +10,7 @@
 
 #include "ethdev_rx_priv.h"
 #include "ethdev_tx_priv.h"
+#include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
 static struct ethdev_ctrl {
@@ -20,14 +21,17 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 		    uint16_t nb_graphs)
 {
+	struct rte_node_register *ip4_rewrite_node;
 	struct ethdev_tx_node_main *tx_node_data;
 	uint16_t tx_q_used, rx_q_used, port_id;
 	struct rte_node_register *tx_node;
 	char name[RTE_NODE_NAMESIZE];
+	const char *next_nodes = name;
 	struct rte_mempool *mp;
+	int i, j, rc;
 	uint32_t id;
-	int i, j;
 
+	ip4_rewrite_node = ip4_rewrite_node_get();
 	tx_node_data = ethdev_tx_node_data_get();
 	tx_node = ethdev_tx_node_get();
 	for (i = 0; i < nb_confs; i++) {
@@ -92,6 +96,18 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 
 		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
 			 name, id);
+
+		/* Prepare the actual name of the cloned node */
+		snprintf(name, sizeof(name), "ethdev_tx-%u", port_id);
+
+		/* Add this tx port node as next to ip4_rewrite_node */
+		rte_node_edge_update(ip4_rewrite_node->id, RTE_EDGE_ID_INVALID,
+				     &next_nodes, 1);
+		/* Assuming edge id is the last one alloc'ed */
+		rc = ip4_rewrite_set_next(
+			port_id, rte_node_edge_count(ip4_rewrite_node->id) - 1);
+		if (rc < 0)
+			return rc;
 	}
 
 	ctrl.nb_graphs = nb_graphs;
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index c003e9c91..fb7090c40 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -27,6 +27,8 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+static struct ip4_lookup_node_main ip4_lookup_nm;
+
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 /* ARM64 NEON */
 static uint16_t
@@ -524,12 +526,90 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 
 #endif
 
+int
+rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+		       enum rte_node_ip4_lookup_next next_node)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	struct in_addr in;
+	uint8_t socket;
+	uint32_t val;
+	int ret;
+
+	in.s_addr = htonl(ip);
+	inet_ntop(AF_INET, &in, abuf, sizeof(abuf));
+	/* Embedded next node id in next hop */
+	val = (next_node << 16) | next_hop;
+	node_dbg("ip4_lookup", "LPM: Adding route %s / %d nh (0x%x)", abuf,
+		 depth, val);
+
+	for (socket = 0; socket < RTE_MAX_NUMA_NODES; socket++) {
+		if (!ip4_lookup_nm.lpm_tbl[socket])
+			continue;
+
+		ret = rte_lpm_add(ip4_lookup_nm.lpm_tbl[socket], ip, depth,
+				  val);
+
+		if (ret < 0) {
+			node_err("ip4_lookup",
+				 "Unable to add entry %s / %d nh (%x) to LPM table on sock %d, rc=%d\n",
+				 abuf, depth, val, socket, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int
+setup_lpm(struct ip4_lookup_node_main *nm, int socket)
+{
+	struct rte_lpm_config config_ipv4;
+	char s[64];
+
+	/* One LPM table per socket */
+	if (nm->lpm_tbl[socket])
+		return 0;
+
+	/* create the LPM table */
+	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
+	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
+	config_ipv4.flags = 0;
+	snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socket);
+	nm->lpm_tbl[socket] = rte_lpm_create(s, socket, &config_ipv4);
+	if (nm->lpm_tbl[socket] == NULL)
+		return -rte_errno;
+
+	return 0;
+}
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
+	struct rte_lpm **lpm_p = (struct rte_lpm **)&node->ctx;
+	uint16_t socket, lcore_id;
+	static uint8_t init_once;
+	int rc;
+
 	RTE_SET_USED(graph);
 	RTE_SET_USED(node);
 
+	if (!init_once) {
+		/* Setup LPM tables for all sockets */
+		RTE_LCORE_FOREACH(lcore_id)
+		{
+			socket = rte_lcore_to_socket_id(lcore_id);
+			rc = setup_lpm(&ip4_lookup_nm, socket);
+			if (rc) {
+				node_err("ip4_lookup",
+					 "Failed to setup lpm tbl for sock %u, rc=%d",
+					 socket, rc);
+				return rc;
+			}
+		}
+		init_once = 1;
+	}
+	*lpm_p = ip4_lookup_nm.lpm_tbl[graph->socket];
 	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
 
 	return 0;
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
index f317f7f4f..14b908ec9 100644
--- a/lib/librte_node/ip4_rewrite.c
+++ b/lib/librte_node/ip4_rewrite.c
@@ -255,6 +255,56 @@ ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 	return 0;
 }
 
+int
+ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index)
+{
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+	ip4_rewrite_nm->next_index[port_id] = next_index;
+
+	return 0;
+}
+
+int
+rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			 uint8_t rewrite_len, uint16_t dst_port)
+{
+	struct ip4_rewrite_nh_header *nh;
+
+	if (next_hop >= RTE_GRAPH_IP4_REWRITE_MAX_NH)
+		return -EINVAL;
+
+	if (rewrite_len > RTE_GRAPH_IP4_REWRITE_MAX_LEN)
+		return -EINVAL;
+
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+
+	/* Check if dst port doesn't exist as edge */
+	if (!ip4_rewrite_nm->next_index[dst_port])
+		return -EINVAL;
+
+	/* Update next hop */
+	nh = &ip4_rewrite_nm->nh[next_hop];
+
+	memcpy(nh->rewrite_data, rewrite_data, rewrite_len);
+	nh->tx_node = ip4_rewrite_nm->next_index[dst_port];
+	nh->rewrite_len = rewrite_len;
+	nh->enabled = true;
+
+	return 0;
+}
+
 static struct rte_node_register ip4_rewrite_node = {
 	.process = ip4_rewrite_node_process,
 	.name = "ip4_rewrite",
@@ -266,4 +316,10 @@ static struct rte_node_register ip4_rewrite_node = {
 	.init = ip4_rewrite_node_init,
 };
 
+struct rte_node_register *
+ip4_rewrite_node_get(void)
+{
+	return &ip4_rewrite_node;
+}
+
 RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
index 420996a03..80f0abdc9 100644
--- a/lib/librte_node/ip4_rewrite_priv.h
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -48,6 +48,28 @@ struct ip4_rewrite_node_main {
 	/**< Next index of each configured port. */
 };
 
+/**
+ * @internal
+ *
+ * Get the ipv4 rewrite node.
+ *
+ * @retrun
+ *   Pointer to the ipv4 rewrite node.
+ */
+struct rte_node_register *ip4_rewrite_node_get(void);
+
+/**
+ * @internal
+ *
+ * Set the Edge index of a given port_id.
+ *
+ * @param port_id
+ *   Ethernet port identifier.
+ * @param next_index
+ *   Edge index of the Given Tx node.
+ */
+int ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
index 37c12bf82..394cac097 100644
--- a/lib/librte_node/rte_node_ip4_api.h
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -36,6 +36,50 @@ enum rte_node_ip4_lookup_next {
 	/**< Number of next nodes of lookup node. */
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add ipv4 route to lookup table.
+ *
+ * @param ip
+ *   IP address of route to be added.
+ * @param depth
+ *   Depth of the rule to be added.
+ * @param next_hop
+ *   Next hop id of the rule result to be added.
+ * @param next_node
+ *   Next node to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+			   enum rte_node_ip4_lookup_next next_node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add a next hop's rewrite data.
+ *
+ * @param next_hop
+ *   Next hop id to add rewrite data to.
+ * @param rewrite_data
+ *   Rewrite data.
+ * @param rewrite_len
+ *   Length of rewrite data.
+ * @param dst_port
+ *   Destination port to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			     uint8_t rewrite_len, uint16_t dst_port);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index c6c71bd02..a799b0d38 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -2,6 +2,8 @@ EXPERIMENTAL {
 	global:
 
 	rte_node_eth_config;
+	rte_node_ip4_route_add;
+	rte_node_ip4_rewrite_add;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v1 23/26] node: add pkt drop node
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (21 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 22/26] node: add ipv4 rewrite and lookup ctrl API jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 24/26] l3fwd-graph: add graph based l3fwd skeleton jerinj
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add pkt drop node process function for pkt_drop
rte_node. This node simply free's every object received as
an rte_mbuf to its rte_pktmbuf pool.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile    |  1 +
 lib/librte_node/meson.build |  2 +-
 lib/librte_node/pkt_drop.c  | 26 ++++++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/pkt_drop.c

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index be7e7e338..aaf041580 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -21,6 +21,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += pkt_drop.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index ef056d9af..59e11e5b4 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ip4_rewrite.c', 'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'pkt_drop.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
diff --git a/lib/librte_node/pkt_drop.c b/lib/librte_node/pkt_drop.c
new file mode 100644
index 000000000..c35001323
--- /dev/null
+++ b/lib/librte_node/pkt_drop.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_graph.h>
+#include <rte_mbuf.h>
+
+static uint16_t
+pkt_drop_process(struct rte_graph *graph, struct rte_node *node, void **objs,
+		 uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(graph);
+
+	rte_pktmbuf_free_bulk((struct rte_mbuf **)objs, nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register pkt_drop_node = {
+	.process = pkt_drop_process,
+	.name = "pkt_drop",
+};
+
+RTE_NODE_REGISTER(pkt_drop_node);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 24/26] l3fwd-graph: add graph based l3fwd skeleton
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (22 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 23/26] node: add pkt drop node jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 25/26] l3fwd-graph: add ethdev configuration changes jerinj
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Thomas Monjalon, Marko Kovacevic, Ori Kam, Bruce Richardson,
	Radu Nicolau, Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori,
	Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph based l3fwd application skeleton with cmdline
parsing support inline with normal l3fwd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                      |   3 +
 examples/Makefile                |   3 +
 examples/l3fwd-graph/Makefile    |  58 ++++
 examples/l3fwd-graph/main.c      | 528 +++++++++++++++++++++++++++++++
 examples/l3fwd-graph/meson.build |  13 +
 examples/meson.build             |   6 +-
 6 files changed, 609 insertions(+), 2 deletions(-)
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index 3959ed19a..73debcd6d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1580,6 +1580,9 @@ F: doc/guides/sample_app_ug/l2_forward_event.rst
 F: examples/l3fwd/
 F: doc/guides/sample_app_ug/l3_forward.rst
 
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+F: examples/l3fwd-graph/
+
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
 
diff --git a/examples/Makefile b/examples/Makefile
index feff79784..b7e99a2f7 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -51,6 +51,9 @@ DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl
 ifeq ($(CONFIG_RTE_LIBRTE_LPM)$(CONFIG_RTE_LIBRTE_HASH),yy)
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH),y)
+DIRS-y += l3fwd-graph
+endif
 DIRS-y += link_status_interrupt
 DIRS-y += multi_process
 DIRS-y += ntb
diff --git a/examples/l3fwd-graph/Makefile b/examples/l3fwd-graph/Makefile
new file mode 100644
index 000000000..f3cf275ec
--- /dev/null
+++ b/examples/l3fwd-graph/Makefile
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# binary name
+APP = l3fwd-graph
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+# Build using pkg-config variables if possible
+ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)
+
+all: shared
+.PHONY: shared static
+shared: build/$(APP)-shared
+	ln -sf $(APP)-shared build/$(APP)
+static: build/$(APP)-static
+	ln -sf $(APP)-static build/$(APP)
+
+PKGCONF=pkg-config --define-prefix
+
+PC_FILE := $(shell $(PKGCONF) --path libdpdk)
+CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) -DALLOW_EXPERIMENTAL_API
+LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
+LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)
+
+build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
+
+build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
+
+build:
+	@mkdir -p $@
+
+.PHONY: clean
+clean:
+	rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
+	test -d build && rmdir -p build || true
+
+else # Build using legacy build system
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, detect a build directory, by looking for a path with a .config
+RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -O3 $(USER_FLAGS)
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+endif
diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
new file mode 100644
index 000000000..8cee8bebd
--- /dev/null
+++ b/examples/l3fwd-graph/main.c
@@ -0,0 +1,528 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+
+#include <rte_branch_prediction.h>
+#include <rte_common.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_per_lcore.h>
+#include <rte_string_fns.h>
+#include <rte_vect.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
+
+/* Log type */
+#define RTE_LOGTYPE_L3FWD_GRAPH RTE_LOGTYPE_USER1
+
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 1024
+#define RTE_TEST_TX_DESC_DEFAULT 1024
+
+#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
+#define MAX_RX_QUEUE_PER_PORT 128
+
+#define MAX_RX_QUEUE_PER_LCORE 16
+
+#define MAX_LCORE_PARAMS 1024
+
+#define NB_SOCKETS 8
+
+/**< Ports set in promiscuous mode off by default. */
+static int promiscuous_on;
+
+static int numa_on = 1;	  /**< NUMA is enabled by default. */
+static int per_port_pool; /**< Use separate buffer pools per port; disabled */
+			  /**< by default */
+
+static volatile bool force_quit;
+
+/* Ethernet addresses of ports */
+static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+xmm_t val_eth[RTE_MAX_ETHPORTS];
+
+/* Mask of enabled ports */
+static uint32_t enabled_port_mask;
+
+struct lcore_rx_queue {
+	uint16_t port_id;
+	uint8_t queue_id;
+};
+
+/* Lcore conf */
+struct lcore_conf {
+	uint16_t n_rx_queue;
+	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+} __rte_cache_aligned;
+
+static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+
+struct lcore_params {
+	uint16_t port_id;
+	uint8_t queue_id;
+	uint8_t lcore_id;
+} __rte_cache_aligned;
+
+static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
+static struct lcore_params lcore_params_array_default[] = {
+	{0, 0, 2}, {0, 1, 2}, {0, 2, 2}, {1, 0, 2}, {1, 1, 2},
+	{1, 2, 2}, {2, 0, 2}, {3, 0, 3}, {3, 1, 3},
+};
+
+static struct lcore_params *lcore_params = lcore_params_array_default;
+static uint16_t nb_lcore_params = RTE_DIM(lcore_params_array_default);
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_RSS,
+		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+	},
+	.rx_adv_conf = {
+		.rss_conf = {
+				.rss_key = NULL,
+				.rss_hf = ETH_RSS_IP,
+		},
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static int
+check_lcore_params(void)
+{
+	uint8_t queue, lcore;
+	int socketid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		queue = lcore_params[i].queue_id;
+		if (queue >= MAX_RX_QUEUE_PER_PORT) {
+			printf("Invalid queue number: %hhu\n", queue);
+			return -1;
+		}
+		lcore = lcore_params[i].lcore_id;
+		if (!rte_lcore_is_enabled(lcore)) {
+			printf("Error: lcore %hhu is not enabled in lcore mask\n",
+			       lcore);
+			return -1;
+		}
+
+		if (lcore == rte_get_master_lcore()) {
+			printf("Error: lcore %u is master lcore\n", lcore);
+			return -1;
+		}
+		socketid = rte_lcore_to_socket_id(lcore);
+		if ((socketid != 0) && (numa_on == 0)) {
+			printf("Warning: lcore %hhu is on socket %d with numa off\n",
+			       lcore, socketid);
+		}
+	}
+
+	return 0;
+}
+
+static int
+check_port_config(void)
+{
+	uint16_t portid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		portid = lcore_params[i].port_id;
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("Port %u is not enabled in port mask\n", portid);
+			return -1;
+		}
+		if (!rte_eth_dev_is_valid_port(portid)) {
+			printf("Port %u is not present on the board\n", portid);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+init_lcore_rx_queues(void)
+{
+	uint16_t i, nb_rx_queue;
+	uint8_t lcore;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		lcore = lcore_params[i].lcore_id;
+		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
+			printf("Error: too many queues (%u) for lcore: %u\n",
+			       (unsigned int)nb_rx_queue + 1,
+			       (unsigned int)lcore);
+			return -1;
+		}
+
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+			lcore_params[i].port_id;
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+			lcore_params[i].queue_id;
+		lcore_conf[lcore].n_rx_queue++;
+	}
+
+	return 0;
+}
+
+/* Display usage */
+static void
+print_usage(const char *prgname)
+{
+	fprintf(stderr,
+		"%s [EAL options] --"
+		" -p PORTMASK"
+		" [-P]"
+		" [-E]"
+		" [-L]"
+		" --config (port,queue,lcore)[,(port,queue,lcore)]"
+		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
+		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
+		" [--no-numa]"
+		" [--per-port-pool]\n\n"
+
+		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
+		"  -P : Enable promiscuous mode\n"
+		"  --config (port,queue,lcore): Rx queue configuration\n"
+		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for "
+		"port X\n"
+		"  --enable-jumbo: Enable jumbo frames\n"
+		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"                 maximum packet length in decimal (64-9600)\n"
+		"  --no-numa: Disable numa awareness\n"
+		"  --per-port-pool: Use separate buffer pool per port\n\n",
+		prgname);
+}
+
+static int
+parse_max_pkt_len(const char *pktlen)
+{
+	unsigned long len;
+	char *end = NULL;
+
+	/* Parse decimal string */
+	len = strtoul(pktlen, &end, 10);
+	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (len == 0)
+		return -1;
+
+	return len;
+}
+
+static int
+parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_config(const char *q_arg)
+{
+	enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD };
+	unsigned long int_fld[_NUM_FLD];
+	const char *p, *p0 = q_arg;
+	char *str_fld[_NUM_FLD];
+	uint32_t size;
+	char s[256];
+	char *end;
+	int i;
+
+	nb_lcore_params = 0;
+
+	while ((p = strchr(p0, '(')) != NULL) {
+		++p;
+		p0 = strchr(p, ')');
+		if (p0 == NULL)
+			return -1;
+
+		size = p0 - p;
+		if (size >= sizeof(s))
+			return -1;
+
+		snprintf(s, sizeof(s), "%.*s", size, p);
+		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
+		    _NUM_FLD)
+			return -1;
+		for (i = 0; i < _NUM_FLD; i++) {
+			errno = 0;
+			int_fld[i] = strtoul(str_fld[i], &end, 0);
+			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
+				return -1;
+		}
+		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
+			printf("Exceeded max number of lcore params: %hu\n",
+			       nb_lcore_params);
+			return -1;
+		}
+		lcore_params_array[nb_lcore_params].port_id =
+			(uint8_t)int_fld[FLD_PORT];
+		lcore_params_array[nb_lcore_params].queue_id =
+			(uint8_t)int_fld[FLD_QUEUE];
+		lcore_params_array[nb_lcore_params].lcore_id =
+			(uint8_t)int_fld[FLD_LCORE];
+		++nb_lcore_params;
+	}
+	lcore_params = lcore_params_array;
+
+	return 0;
+}
+
+static void
+parse_eth_dest(const char *optarg)
+{
+	uint8_t c, *dest, peer_addr[6];
+	uint16_t portid;
+	char *port_end;
+
+	errno = 0;
+	portid = strtoul(optarg, &port_end, 10);
+	if (errno != 0 || port_end == optarg || *port_end++ != ',')
+		rte_exit(EXIT_FAILURE, "Invalid eth-dest: %s", optarg);
+	if (portid >= RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE,
+			 "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", portid,
+			 RTE_MAX_ETHPORTS);
+
+	if (cmdline_parse_etheraddr(NULL, port_end, &peer_addr,
+				    sizeof(peer_addr)) < 0)
+		rte_exit(EXIT_FAILURE, "Invalid ethernet address: %s\n",
+			 port_end);
+	dest = (uint8_t *)&dest_eth_addr[portid];
+	for (c = 0; c < 6; c++)
+		dest[c] = peer_addr[c];
+	*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+}
+
+#define MAX_JUMBO_PKT_LEN  9600
+#define MEMPOOL_CACHE_SIZE 256
+
+static const char short_options[] = "p:" /* portmask */
+				    "P"	 /* promiscuous */
+				    "L"	 /* enable long prefix match */
+				    "E"	 /* enable exact match */
+	;
+
+#define CMD_LINE_OPT_CONFIG	   "config"
+#define CMD_LINE_OPT_ETH_DEST	   "eth-dest"
+#define CMD_LINE_OPT_NO_NUMA	   "no-numa"
+#define CMD_LINE_OPT_ENABLE_JUMBO  "enable-jumbo"
+#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool"
+enum {
+	/* Long options mapped to a short option */
+
+	/* First long only option value must be >= 256, so that we won't
+	 * conflict with short options
+	 */
+	CMD_LINE_OPT_MIN_NUM = 256,
+	CMD_LINE_OPT_CONFIG_NUM,
+	CMD_LINE_OPT_ETH_DEST_NUM,
+	CMD_LINE_OPT_NO_NUMA_NUM,
+	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
+	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
+};
+
+static const struct option lgopts[] = {
+	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL},
+	{NULL, 0, 0, 0},
+};
+
+/*
+ * This expression is used to calculate the number of mbufs needed
+ * depending on user input, taking  into account memory for rx and
+ * tx hardware rings, cache per lcore and mtable per port per lcore.
+ * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum
+ * value of 8192
+ */
+#define NB_MBUF(nports)                                                        \
+	RTE_MAX((nports * nb_rx_queue * nb_rxd +                               \
+		 nports * nb_lcores * RTE_GRAPH_BURST_SIZE +                   \
+		 nports * n_tx_queue * nb_txd +                                \
+		 nb_lcores * MEMPOOL_CACHE_SIZE), 8192u)
+
+/* Parse the argument given in the command line of the application */
+static int
+parse_args(int argc, char **argv)
+{
+	char *prgname = argv[0];
+	int option_index;
+	char **argvopt;
+	int opt, ret;
+
+	argvopt = argv;
+
+	/* Error or normal output strings. */
+	while ((opt = getopt_long(argc, argvopt, short_options, lgopts,
+				  &option_index)) != EOF) {
+
+		switch (opt) {
+		/* Portmask */
+		case 'p':
+			enabled_port_mask = parse_portmask(optarg);
+			if (enabled_port_mask == 0) {
+				fprintf(stderr, "Invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case 'P':
+			promiscuous_on = 1;
+			break;
+
+		/* Long options */
+		case CMD_LINE_OPT_CONFIG_NUM:
+			ret = parse_config(optarg);
+			if (ret) {
+				fprintf(stderr, "Invalid config\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case CMD_LINE_OPT_ETH_DEST_NUM:
+			parse_eth_dest(optarg);
+			break;
+
+		case CMD_LINE_OPT_NO_NUMA_NUM:
+			numa_on = 0;
+			break;
+
+		case CMD_LINE_OPT_ENABLE_JUMBO_NUM: {
+			const struct option lenopts = {"max-pkt-len",
+						       required_argument, 0, 0};
+
+			port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME;
+			port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS;
+
+			/*
+			 * if no max-pkt-len set, use the default
+			 * value RTE_ETHER_MAX_LEN.
+			 */
+			if (getopt_long(argc, argvopt, "", &lenopts,
+					&option_index) == 0) {
+				ret = parse_max_pkt_len(optarg);
+				if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) {
+					fprintf(stderr, "Invalid maximum "
+							"packet length\n");
+					print_usage(prgname);
+					return -1;
+				}
+				port_conf.rxmode.max_rx_pkt_len = ret;
+			}
+			break;
+		}
+
+		case CMD_LINE_OPT_PARSE_PER_PORT_POOL:
+			printf("Per port buffer pool is enabled\n");
+			per_port_pool = 1;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind - 1] = prgname;
+	ret = optind - 1;
+	optind = 1; /* Reset getopt lib */
+
+	return ret;
+}
+
+static void
+signal_handler(int signum)
+{
+	if (signum == SIGINT || signum == SIGTERM) {
+		printf("\n\nSignal %d received, preparing to exit...\n",
+		       signum);
+		force_quit = true;
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	uint16_t portid;
+	int ret;
+
+	/* Init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+	argc -= ret;
+	argv += ret;
+
+	force_quit = false;
+	signal(SIGINT, signal_handler);
+	signal(SIGTERM, signal_handler);
+
+	/* Pre-init dst MACs for all ports to 02:00:00:00:00:xx */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		dest_eth_addr[portid] =
+			RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40);
+		*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+	}
+
+	/* Parse application arguments (after the EAL ones) */
+	ret = parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L3FWD_GRAPH parameters\n");
+
+	if (check_lcore_params() < 0)
+		rte_exit(EXIT_FAILURE, "check_lcore_params() failed\n");
+
+	ret = init_lcore_rx_queues();
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues() failed\n");
+
+	if (check_port_config() < 0)
+		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
+
+	printf("Bye...\n");
+
+	return ret;
+}
diff --git a/examples/l3fwd-graph/meson.build b/examples/l3fwd-graph/meson.build
new file mode 100644
index 000000000..a816bd890
--- /dev/null
+++ b/examples/l3fwd-graph/meson.build
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+deps += ['graph', 'eal', 'lpm', 'ethdev', 'node' ]
+sources = files(
+	'main.c'
+)
+allow_experimental_apis = true
diff --git a/examples/meson.build b/examples/meson.build
index 1f2b6f516..3b540012f 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -2,8 +2,10 @@
 # Copyright(c) 2017-2019 Intel Corporation
 
 driver_libs = []
+node_libs = []
 if get_option('default_library') == 'static'
 	driver_libs = dpdk_drivers
+	node_libs = dpdk_graph_nodes
 endif
 
 execinfo = cc.find_library('execinfo', required: false)
@@ -23,7 +25,7 @@ all_examples = [
 	'l2fwd', 'l2fwd-cat', 'l2fwd-event',
 	'l2fwd-crypto', 'l2fwd-jobstats',
 	'l2fwd-keepalive', 'l3fwd',
-	'l3fwd-acl', 'l3fwd-power',
+	'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph',
 	'link_status_interrupt',
 	'multi_process/client_server_mp/mp_client',
 	'multi_process/client_server_mp/mp_server',
@@ -99,7 +101,7 @@ foreach example: examples
 		endif
 		executable('dpdk-' + name, sources,
 			include_directories: includes,
-			link_whole: driver_libs,
+			link_whole: driver_libs + node_libs,
 			link_args: dpdk_extra_ldflags,
 			c_args: cflags,
 			dependencies: dep_objs)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 25/26] l3fwd-graph: add ethdev configuration changes
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (23 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 24/26] l3fwd-graph: add graph based l3fwd skeleton jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 26/26] l3fwd-graph: add graph config and main loop jerinj
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add changes to ethdev port and queue configuration based
on command line parameters for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 350 +++++++++++++++++++++++++++++++++++-
 1 file changed, 349 insertions(+), 1 deletion(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index 8cee8bebd..436987877 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -19,8 +19,10 @@
 
 #include <rte_branch_prediction.h>
 #include <rte_common.h>
+#include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
 #include <rte_per_lcore.h>
@@ -48,6 +50,10 @@
 
 #define NB_SOCKETS 8
 
+/* Static global variables used within this file. */
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
@@ -59,6 +65,7 @@ static volatile bool force_quit;
 
 /* Ethernet addresses of ports */
 static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+static struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
 xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 /* Mask of enabled ports */
@@ -109,6 +116,8 @@ static struct rte_eth_conf port_conf = {
 	},
 };
 
+static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
+
 static int
 check_lcore_params(void)
 {
@@ -164,6 +173,27 @@ check_port_config(void)
 	return 0;
 }
 
+static uint8_t
+get_port_n_rx_queues(const uint16_t port)
+{
+	int queue = -1;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		if (lcore_params[i].port_id == port) {
+			if (lcore_params[i].queue_id == queue + 1)
+				queue = lcore_params[i].queue_id;
+			else
+				rte_exit(EXIT_FAILURE,
+					 "Queue ids of the port %d must be"
+					 " in sequence and must start with 0\n",
+					 lcore_params[i].port_id);
+		}
+	}
+
+	return (uint8_t)(++queue);
+}
+
 static int
 init_lcore_rx_queues(void)
 {
@@ -473,6 +503,120 @@ parse_args(int argc, char **argv)
 	return ret;
 }
 
+static void
+print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
+{
+	char buf[RTE_ETHER_ADDR_FMT_SIZE];
+	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
+	printf("%s%s", name, buf);
+}
+
+static int
+init_mem(uint16_t portid, uint32_t nb_mbuf)
+{
+	uint32_t lcore_id;
+	int socketid;
+	char s[64];
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		if (numa_on)
+			socketid = rte_lcore_to_socket_id(lcore_id);
+		else
+			socketid = 0;
+
+		if (socketid >= NB_SOCKETS) {
+			rte_exit(EXIT_FAILURE,
+				 "Socket %d of lcore %u is out of range %d\n",
+				 socketid, lcore_id, NB_SOCKETS);
+		}
+
+		if (pktmbuf_pool[portid][socketid] == NULL) {
+			snprintf(s, sizeof(s), "mbuf_pool_%d:%d", portid,
+				 socketid);
+			/* Create a pool with priv size of a cacheline */
+			pktmbuf_pool[portid][socketid] =
+				rte_pktmbuf_pool_create(
+					s, nb_mbuf, MEMPOOL_CACHE_SIZE,
+					RTE_CACHE_LINE_SIZE,
+					RTE_MBUF_DEFAULT_BUF_SIZE, socketid);
+			if (pktmbuf_pool[portid][socketid] == NULL)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init mbuf pool on socket %d\n",
+					 socketid);
+			else
+				printf("Allocated mbuf pool on socket %d\n",
+				       socketid);
+		}
+	}
+
+	return 0;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90  /* 9s (90 * 100ms) in total */
+	uint8_t count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+	uint16_t portid;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		if (force_quit)
+			return;
+		all_ports_up = 1;
+		RTE_ETH_FOREACH_DEV(portid)
+		{
+			if (force_quit)
+				return;
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(portid, &link);
+			/* Print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status)
+					printf("Port%d Link Up. Speed %u Mbps "
+					       "-%s\n",
+					       portid, link.link_speed,
+					       (link.link_duplex ==
+						ETH_LINK_FULL_DUPLEX)
+						       ? ("full-duplex")
+						       : ("half-duplex\n"));
+				else
+					printf("Port %d Link Down\n", portid);
+				continue;
+			}
+			/* Clear all_ports_up flag if any link down */
+			if (link.link_status == ETH_LINK_DOWN) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* After finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* Set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("Done\n");
+		}
+	}
+}
+
 static void
 signal_handler(int signum)
 {
@@ -486,7 +630,14 @@ signal_handler(int signum)
 int
 main(int argc, char **argv)
 {
-	uint16_t portid;
+	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_eth_dev_info dev_info;
+	uint32_t n_tx_queue, nb_lcores;
+	struct rte_eth_txconf *txconf;
+	uint16_t queueid, portid;
+	struct lcore_conf *qconf;
+	uint32_t lcore_id;
+	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -522,6 +673,203 @@ main(int argc, char **argv)
 	if (check_port_config() < 0)
 		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
 
+	nb_ports = rte_eth_dev_count_avail();
+	nb_lcores = rte_lcore_count();
+
+	/* Initialize all ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		struct rte_eth_conf local_port_conf = port_conf;
+
+		/* Skip ports that are not enabled */
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("\nSkipping disabled port %d\n", portid);
+			continue;
+		}
+
+		/* Init port */
+		printf("Initializing port %d ... ", portid);
+		fflush(stdout);
+
+		nb_rx_queue = get_port_n_rx_queues(portid);
+		n_tx_queue = nb_lcores;
+		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
+			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
+		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
+		       nb_rx_queue, n_tx_queue);
+
+		rte_eth_dev_info_get(portid, &dev_info);
+		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+			local_port_conf.txmode.offloads |=
+				DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
+			dev_info.flow_type_rss_offloads;
+		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
+		    port_conf.rx_adv_conf.rss_conf.rss_hf) {
+			printf("Port %u modified RSS hash function based on "
+			       "hardware support,"
+			       "requested:%#" PRIx64 " configured:%#" PRIx64
+			       "\n",
+			       portid, port_conf.rx_adv_conf.rss_conf.rss_hf,
+			       local_port_conf.rx_adv_conf.rss_conf.rss_hf);
+		}
+
+		ret = rte_eth_dev_configure(portid, nb_rx_queue,
+					    n_tx_queue, &local_port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot configure device: err=%d, port=%d\n",
+				 ret, portid);
+
+		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
+						       &nb_txd);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot adjust number of descriptors: err=%d, "
+				 "port=%d\n",
+				 ret, portid);
+
+		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
+		print_ethaddr(" Address:", &ports_eth_addr[portid]);
+		printf(", ");
+		print_ethaddr(
+			"Destination:",
+			(const struct rte_ether_addr *)&dest_eth_addr[portid]);
+		printf(", ");
+
+		/*
+		 * prepare src MACs for each port.
+		 */
+		rte_ether_addr_copy(
+			&ports_eth_addr[portid],
+			(struct rte_ether_addr *)(val_eth + portid) + 1);
+
+		/* Init memory */
+		if (!per_port_pool) {
+			/* portid = 0; this is *not* signifying the first port,
+			 * rather, it signifies that portid is ignored.
+			 */
+			ret = init_mem(0, NB_MBUF(nb_ports));
+		} else {
+			ret = init_mem(portid, NB_MBUF(1));
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "init_mem() failed\n");
+
+		/* Init one TX queue per couple (lcore,port) */
+		queueid = 0;
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			qconf = &lcore_conf[lcore_id];
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
+			fflush(stdout);
+
+			txconf = &dev_info.default_txconf;
+			txconf->offloads = local_port_conf.txmode.offloads;
+			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+						     socketid, txconf);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_tx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+			queueid++;
+		}
+
+		printf("\n");
+	}
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		qconf = &lcore_conf[lcore_id];
+		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
+		fflush(stdout);
+		/* Init RX queues */
+		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+			struct rte_eth_rxconf rxq_conf;
+
+			portid = qconf->rx_queue_list[queue].port_id;
+			queueid = qconf->rx_queue_list[queue].queue_id;
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
+			fflush(stdout);
+
+			rte_eth_dev_info_get(portid, &dev_info);
+			rxq_conf = dev_info.default_rxconf;
+			rxq_conf.offloads = port_conf.rxmode.offloads;
+			if (!per_port_pool)
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf, pktmbuf_pool[0][socketid]);
+			else
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf,
+					pktmbuf_pool[portid][socketid]);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_rx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+
+		}
+	}
+
+	printf("\n");
+
+	/* Start ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* Start device */
+		ret = rte_eth_dev_start(portid);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "rte_eth_dev_start: err=%d, port=%d\n", ret,
+				 portid);
+
+		/*
+		 * If enabled, put device in promiscuous mode.
+		 * This allows IO forwarding mode to forward packets
+		 * to itself through 2 cross-connected  ports of the
+		 * target machine.
+		 */
+		if (promiscuous_on)
+			rte_eth_promiscuous_enable(portid);
+	}
+
+	printf("\n");
+
+	check_all_ports_link_status(enabled_port_mask);
+
+	/* Stop ports */
+	RTE_ETH_FOREACH_DEV(portid) {
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("Closing port %d...", portid);
+		rte_eth_dev_stop(portid);
+		rte_eth_dev_close(portid);
+		printf(" Done\n");
+	}
 	printf("Bye...\n");
 
 	return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v1 26/26] l3fwd-graph: add graph config and main loop
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (24 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 25/26] l3fwd-graph: add ethdev configuration changes jerinj
@ 2020-03-18 21:35 ` jerinj
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
  26 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-18 21:35 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph creation, configuration logic and graph main loop.
This graph main loop is run on every slave lcore and calls
rte_graph_walk() to walk over lcore specific rte_graph.
Master core accumulates and prints graph walk stats of all the
lcore's graph's.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 241 +++++++++++++++++++++++++++++++++++-
 1 file changed, 239 insertions(+), 2 deletions(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index 436987877..104a4d8db 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -22,9 +22,13 @@
 #include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_graph_worker.h>
+#include <rte_launch.h>
 #include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
+#include <rte_node_eth_api.h>
+#include <rte_node_ip4_api.h>
 #include <rte_per_lcore.h>
 #include <rte_string_fns.h>
 #include <rte_vect.h>
@@ -74,12 +78,17 @@ static uint32_t enabled_port_mask;
 struct lcore_rx_queue {
 	uint16_t port_id;
 	uint8_t queue_id;
+	char node_name[RTE_NODE_NAMESIZE];
 };
 
 /* Lcore conf */
 struct lcore_conf {
 	uint16_t n_rx_queue;
 	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+
+	struct rte_graph *graph;
+	char name[RTE_GRAPH_NAMESIZE];
+	rte_graph_t graph_id;
 } __rte_cache_aligned;
 
 static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
@@ -118,6 +127,25 @@ static struct rte_eth_conf port_conf = {
 
 static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
 
+static struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS];
+
+struct ipv4_l3fwd_lpm_route {
+	uint32_t ip;
+	uint8_t depth;
+	uint8_t if_out;
+};
+
+#define IPV4_L3FWD_LPM_NUM_ROUTES                                              \
+	(sizeof(ipv4_l3fwd_lpm_route_array) /                                  \
+	 sizeof(ipv4_l3fwd_lpm_route_array[0]))
+/* 198.18.0.0/16 are set aside for RFC2544 benchmarking. */
+static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
+	{RTE_IPV4(198, 18, 0, 0), 24, 0}, {RTE_IPV4(198, 18, 1, 0), 24, 1},
+	{RTE_IPV4(198, 18, 2, 0), 24, 2}, {RTE_IPV4(198, 18, 3, 0), 24, 3},
+	{RTE_IPV4(198, 18, 4, 0), 24, 4}, {RTE_IPV4(198, 18, 5, 0), 24, 5},
+	{RTE_IPV4(198, 18, 6, 0), 24, 6}, {RTE_IPV4(198, 18, 7, 0), 24, 7},
+};
+
 static int
 check_lcore_params(void)
 {
@@ -627,17 +655,87 @@ signal_handler(int signum)
 	}
 }
 
+static void
+print_stats(void)
+{
+	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+	const char clr[] = {27, '[', '2', 'J', '\0'};
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker_*";
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL)
+		rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
+
+	while (!force_quit) {
+		/* Clear screen and move to top left */
+		printf("%s%s", clr, topLeft);
+		rte_graph_cluster_stats_get(stats, 0);
+		rte_delay_ms(1E3);
+	}
+
+	rte_graph_cluster_stats_destroy(stats);
+}
+
+/* Main processing loop */
+static int
+graph_main_loop(void *conf)
+{
+	struct lcore_conf *qconf;
+	struct rte_graph *graph;
+	uint32_t lcore_id;
+
+	RTE_SET_USED(conf);
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_conf[lcore_id];
+	graph = qconf->graph;
+
+	if (!graph) {
+		RTE_LOG(INFO, L3FWD_GRAPH, "Lcore %u has nothing to do\n",
+			lcore_id);
+		return 0;
+	}
+
+	RTE_LOG(INFO, L3FWD_GRAPH,
+		"Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+		qconf->name, graph);
+
+	while (likely(!force_quit))
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
 int
 main(int argc, char **argv)
 {
+	const char *node_patterns[64] = {
+		"ip4*",
+		"ethdev_tx-*",
+		"pkt_drop",
+	};
+	uint8_t rewrite_data[2 * sizeof(struct rte_ether_addr)];
 	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_graph_param graph_conf;
 	struct rte_eth_dev_info dev_info;
+	uint32_t nb_ports, nb_conf = 0;
 	uint32_t n_tx_queue, nb_lcores;
 	struct rte_eth_txconf *txconf;
-	uint16_t queueid, portid;
+	uint16_t queueid, portid, i;
 	struct lcore_conf *qconf;
+	uint16_t nb_patterns = 3;
+	uint16_t nb_graphs = 0;
+	uint8_t rewrite_len;
 	uint32_t lcore_id;
-	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -786,6 +884,18 @@ main(int argc, char **argv)
 			queueid++;
 		}
 
+		/* Setup ethdev node config */
+		ethdev_conf[nb_conf].port_id = portid;
+		ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+		ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+		if (!per_port_pool)
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+
+		else
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+		ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+		nb_conf++;
 		printf("\n");
 	}
 
@@ -829,11 +939,26 @@ main(int argc, char **argv)
 					 "port=%d\n",
 					 ret, portid);
 
+			/* Add this queue node to its graph */
+			snprintf(qconf->rx_queue_list[queue].node_name,
+				 RTE_NODE_NAMESIZE, "ethdev_rx-%u-%u", portid,
+				 queueid);
+		}
+
+		/* Alloc a graph to this lcore only if source exists  */
+		if (qconf->n_rx_queue) {
+			qconf->graph_id = nb_graphs;
+			nb_graphs++;
 		}
 	}
 
 	printf("\n");
 
+	/* Ethdev node config, skip rx queue mapping */
+	ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+	if (ret)
+		rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", ret);
+
 	/* Start ports */
 	RTE_ETH_FOREACH_DEV(portid)
 	{
@@ -861,6 +986,118 @@ main(int argc, char **argv)
 
 	check_all_ports_link_status(enabled_port_mask);
 
+	/* Graph Initialization */
+	memset(&graph_conf, 0, sizeof(graph_conf));
+	graph_conf.node_patterns = node_patterns;
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		rte_graph_t graph_id;
+		rte_edge_t i;
+
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		qconf = &lcore_conf[lcore_id];
+
+		/* Skip graph creation if no source exists */
+		if (!qconf->n_rx_queue)
+			continue;
+
+		/* Add rx node patterns of this lcore */
+		for (i = 0; i < qconf->n_rx_queue; i++) {
+			graph_conf.node_patterns[nb_patterns + i] =
+				qconf->rx_queue_list[i].node_name;
+		}
+
+		graph_conf.nb_node_patterns = nb_patterns + i;
+		graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+		snprintf(qconf->name, sizeof(qconf->name), "worker_%u",
+			 lcore_id);
+
+		graph_id = rte_graph_create(qconf->name, &graph_conf);
+		if (graph_id != qconf->graph_id)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_create(): graph_id=%d not "
+				 " as expected for lcore %u(%u\n",
+				 graph_id, lcore_id, qconf->graph_id);
+
+		qconf->graph = rte_graph_lookup(qconf->name);
+		if (!qconf->graph)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_lookup(): graph %s not found\n",
+				 qconf->name);
+	}
+
+	memset(&rewrite_data, 0, sizeof(rewrite_data));
+	rewrite_len = sizeof(rewrite_data);
+
+	/* Add route to ip4 graph infra */
+	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+		char route_str[INET6_ADDRSTRLEN * 4];
+		char abuf[INET6_ADDRSTRLEN];
+		struct in_addr in;
+		uint32_t dst_port;
+		uint16_t next_hop;
+
+		/* Skip unused ports */
+		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
+		     enabled_port_mask) == 0)
+			continue;
+
+		dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+		next_hop = i;
+
+		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
+		snprintf(route_str, sizeof(route_str), "%s / %d (%d)",
+			 inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
+			 ipv4_l3fwd_lpm_route_array[i].depth,
+			 ipv4_l3fwd_lpm_route_array[i].if_out);
+
+		ret = rte_node_ip4_route_add(
+			ipv4_l3fwd_lpm_route_array[i].ip,
+			ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+			RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add ip4 route %s to graph\n",
+				 route_str);
+
+		memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+		/* Add next hop for a given destination */
+		ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+					       rewrite_len, dst_port);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add next hop %u for "
+				 "route %s\n",
+				 next_hop, route_str);
+
+		RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+			route_str, next_hop);
+	}
+
+	/* Launch per-lcore init on every slave lcore */
+	rte_eal_mp_remote_launch(graph_main_loop, NULL, SKIP_MASTER);
+
+	/* Accumulate and print stats on master until exit */
+	if (rte_graph_has_stats_feature())
+		print_stats();
+
+	/* Wait for slave cores to exit */
+	ret = 0;
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		ret = rte_eal_wait_lcore(lcore_id);
+		/* Destroy graph */
+		rte_graph_destroy(lcore_conf[lcore_id].name);
+		if (ret < 0) {
+			ret = -1;
+			break;
+		}
+	}
+
 	/* Stop ports */
 	RTE_ETH_FOREACH_DEV(portid) {
 		if ((enabled_port_mask & (1 << portid)) == 0)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup for x86 jerinj
@ 2020-03-19 12:25   ` Ray Kinsella
  2020-03-19 14:22     ` [dpdk-dev] [EXT] " Pavan Nikhilesh Bhagavatula
  0 siblings, 1 reply; 219+ messages in thread
From: Ray Kinsella @ 2020-03-19 12:25 UTC (permalink / raw)
  To: jerinj, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mattias.ronnblom, kirankumark



On 18/03/2020 21:35, jerinj@marvell.com wrote:
> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
> 
> Add IPv4 lookup process function for ip4_lookup
> rte_node. This node performs LPM lookup using x86_64
> vector supported RTE_LPM API on every packet received
> and forwards it to a next node that is identified by
> lookup result.
> 
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> ---
>  lib/librte_node/ip4_lookup.c | 245 +++++++++++++++++++++++++++++++++++
>  1 file changed, 245 insertions(+)
> 
> diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
> index d7fcd1158..c003e9c91 100644
> --- a/lib/librte_node/ip4_lookup.c
> +++ b/lib/librte_node/ip4_lookup.c
> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
>  	return nb_objs;
>  }
>  
> +#elif defined(RTE_ARCH_X86)
> +
> +/* X86 SSE */
> +static uint16_t
> +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> +			void **objs, uint16_t nb_objs)
> +{
> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
> +	rte_edge_t next0, next1, next2, next3, next_index;
> +	struct rte_ipv4_hdr *ipv4_hdr;
> +	struct rte_ether_hdr *eth_hdr;
> +	uint32_t ip0, ip1, ip2, ip3;
> +	void **to_next, **from;
> +	uint16_t last_spec = 0;
> +	uint16_t n_left_from;
> +	struct rte_lpm *lpm;
> +	uint16_t held = 0;
> +	uint32_t drop_nh;
> +	rte_xmm_t dst;
> +	__m128i dip; /* SSE register */
> +	int rc, i;
> +
> +	/* Speculative next */
> +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
> +	/* Drop node */
> +	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
> +
> +	/* Get socket specific LPM from ctx */
> +	lpm = *((struct rte_lpm **)node->ctx);
> +
> +	pkts = (struct rte_mbuf **)objs;
> +	from = objs;
> +	n_left_from = nb_objs;

I doubt this initial prefetch of the first 4 packets has any benefit. 

> +	if (n_left_from >= 4) {
> +		for (i = 0; i < 4; i++) {
> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
> +						       struct rte_ether_hdr *) +
> +				      1);
> +		}
> +	}
> +
> +	/* Get stream for the speculated next node */
> +	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);

Suggest you don't reuse the hand-unrolling optimization from FD.io VPP. 
I have never found any performance benefit from them, and they make the code unnecessarily verbose. 


> +	while (n_left_from >= 4) {
> +		/* Prefetch next-next mbufs */
> +		if (likely(n_left_from >= 11)) {
> +			rte_prefetch0(pkts[8]);
> +			rte_prefetch0(pkts[9]);
> +			rte_prefetch0(pkts[10]);
> +			rte_prefetch0(pkts[11]);
> +		}
> +
> +		/* Prefetch next mbuf data */
> +		if (likely(n_left_from >= 7)) {
> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
> +						       struct rte_ether_hdr *) +
> +				      1);
> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
> +						       struct rte_ether_hdr *) +
> +				      1);
> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
> +						       struct rte_ether_hdr *) +
> +				      1);
> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
> +						       struct rte_ether_hdr *) +
> +				      1);
> +		}
> +
> +		mbuf0 = pkts[0];
> +		mbuf1 = pkts[1];
> +		mbuf2 = pkts[2];
> +		mbuf3 = pkts[3];
> +
> +		pkts += 4;
> +		n_left_from -= 4;
> +
> +		/* Extract DIP of mbuf0 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
> +		ip0 = ipv4_hdr->dst_addr;
> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
> +
> +		/* Extract DIP of mbuf1 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
> +		ip1 = ipv4_hdr->dst_addr;
> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> +		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr->hdr_checksum;
> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr->time_to_live;
> +
> +		/* Extract DIP of mbuf2 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
> +		ip2 = ipv4_hdr->dst_addr;
> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> +		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr->hdr_checksum;
> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr->time_to_live;
> +
> +		/* Extract DIP of mbuf3 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
> +		ip3 = ipv4_hdr->dst_addr;
> +
> +		/* Prepare for lookup x4 */
> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
> +
> +		/* Byte swap 4 IPV4 addresses. */
> +		const __m128i bswap_mask = _mm_set_epi8(
> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
> +
> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> +		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr->hdr_checksum;
> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr->time_to_live;
> +
> +		/* Perform LPM lookup to get NH and next node */
> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
> +
> +		/* Extract next node id and NH */
> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] & 0xFFFF;
> +		next0 = (dst.u32[0] >> 16);
> +
> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] & 0xFFFF;
> +		next1 = (dst.u32[1] >> 16);
> +
> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] & 0xFFFF;
> +		next2 = (dst.u32[2] >> 16);
> +
> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] & 0xFFFF;
> +		next3 = (dst.u32[3] >> 16);
> +
> +		/* Enqueue four to next node */
> +		rte_edge_t fix_spec =
> +			(next_index ^ next0) | (next_index ^ next1) |
> +			(next_index ^ next2) | (next_index ^ next3);
> +
> +		if (unlikely(fix_spec)) {
> +			/* Copy things successfully speculated till now */
> +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> +			from += last_spec;
> +			to_next += last_spec;
> +			held += last_spec;
> +			last_spec = 0;
> +
> +			/* Next0 */
> +			if (next_index == next0) {
> +				to_next[0] = from[0];
> +				to_next++;
> +				held++;
> +			} else {
> +				rte_node_enqueue_x1(graph, node, next0,
> +						    from[0]);
> +			}
> +
> +			/* Next1 */
> +			if (next_index == next1) {
> +				to_next[0] = from[1];
> +				to_next++;
> +				held++;
> +			} else {
> +				rte_node_enqueue_x1(graph, node, next1,
> +						    from[1]);
> +			}
> +
> +			/* Next2 */
> +			if (next_index == next2) {
> +				to_next[0] = from[2];
> +				to_next++;
> +				held++;
> +			} else {
> +				rte_node_enqueue_x1(graph, node, next2,
> +						    from[2]);
> +			}
> +
> +			/* Next3 */
> +			if (next_index == next3) {
> +				to_next[0] = from[3];
> +				to_next++;
> +				held++;
> +			} else {
> +				rte_node_enqueue_x1(graph, node, next3,
> +						    from[3]);
> +			}
> +
> +			from += 4;
> +
> +		} else {
> +			last_spec += 4;
> +		}
> +	}
> +
> +	while (n_left_from > 0) {
> +		uint32_t next_hop;
> +
> +		mbuf0 = pkts[0];
> +
> +		pkts += 1;
> +		n_left_from -= 1;
> +
> +		/* Extract DIP of mbuf0 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
> +
> +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
> +				    &next_hop);
> +		next_hop = (rc == 0) ? next_hop : drop_nh;
> +
> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop & 0xFFFF;
> +		next0 = (next_hop >> 16);
> +
> +		if (unlikely(next_index ^ next0)) {
> +			/* Copy things successfully speculated till now */
> +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> +			from += last_spec;
> +			to_next += last_spec;
> +			held += last_spec;
> +			last_spec = 0;
> +
> +			rte_node_enqueue_x1(graph, node, next0, from[0]);
> +			from += 1;
> +		} else {
> +			last_spec += 1;
> +		}
> +	}
> +
> +	/* !!! Home run !!! */
> +	if (likely(last_spec == nb_objs)) {
> +		rte_node_next_stream_move(graph, node, next_index);
> +		return nb_objs;
> +	}
> +
> +	held += last_spec;
> +	/* Copy things successfully speculated till now */
> +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> +	rte_node_next_stream_put(graph, node, next_index, held);
> +
> +	return nb_objs;
> +}
> +
>  #else
>  
>  static uint16_t
> 

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-19 12:25   ` Ray Kinsella
@ 2020-03-19 14:22     ` Pavan Nikhilesh Bhagavatula
  2020-03-19 15:50       ` Ray Kinsella
  0 siblings, 1 reply; 219+ messages in thread
From: Pavan Nikhilesh Bhagavatula @ 2020-03-19 14:22 UTC (permalink / raw)
  To: Ray Kinsella, Jerin Jacob Kollanukkaran, Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda

>On 18/03/2020 21:35, jerinj@marvell.com wrote:
>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>
>> Add IPv4 lookup process function for ip4_lookup
>> rte_node. This node performs LPM lookup using x86_64
>> vector supported RTE_LPM API on every packet received
>> and forwards it to a next node that is identified by
>> lookup result.
>>
>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>> ---
>>  lib/librte_node/ip4_lookup.c | 245
>+++++++++++++++++++++++++++++++++++
>>  1 file changed, 245 insertions(+)
>>
>> diff --git a/lib/librte_node/ip4_lookup.c
>b/lib/librte_node/ip4_lookup.c
>> index d7fcd1158..c003e9c91 100644
>> --- a/lib/librte_node/ip4_lookup.c
>> +++ b/lib/librte_node/ip4_lookup.c
>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct rte_graph
>*graph, struct rte_node *node,
>>  	return nb_objs;
>>  }
>>
>> +#elif defined(RTE_ARCH_X86)
>> +
>> +/* X86 SSE */
>> +static uint16_t
>> +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node
>*node,
>> +			void **objs, uint16_t nb_objs)
>> +{
>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
>> +	rte_edge_t next0, next1, next2, next3, next_index;
>> +	struct rte_ipv4_hdr *ipv4_hdr;
>> +	struct rte_ether_hdr *eth_hdr;
>> +	uint32_t ip0, ip1, ip2, ip3;
>> +	void **to_next, **from;
>> +	uint16_t last_spec = 0;
>> +	uint16_t n_left_from;
>> +	struct rte_lpm *lpm;
>> +	uint16_t held = 0;
>> +	uint32_t drop_nh;
>> +	rte_xmm_t dst;
>> +	__m128i dip; /* SSE register */
>> +	int rc, i;
>> +
>> +	/* Speculative next */
>> +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>> +	/* Drop node */
>> +	drop_nh =
>((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>> +
>> +	/* Get socket specific LPM from ctx */
>> +	lpm = *((struct rte_lpm **)node->ctx);
>> +
>> +	pkts = (struct rte_mbuf **)objs;
>> +	from = objs;
>> +	n_left_from = nb_objs;
>
>I doubt this initial prefetch of the first 4 packets has any benefit.

Ack will remove in v2 for x86.

>
>> +	if (n_left_from >= 4) {
>> +		for (i = 0; i < 4; i++) {
>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>> +						       struct rte_ether_hdr
>*) +
>> +				      1);
>> +		}
>> +	}
>> +
>> +	/* Get stream for the speculated next node */
>> +	to_next = rte_node_next_stream_get(graph, node,
>next_index, nb_objs);
>
>Suggest you don't reuse the hand-unrolling optimization from FD.io
>VPP.
>I have never found any performance benefit from them, and they
>make the code unnecessarily verbose.
>

How would be take the benefit of rte_lpm_lookupx4 without unrolling the loop?.
Also, in future if we are using rte_rib and fib with a CPU supporting wider SIMD we might
need to unroll them further (AVX256 AND 512 currently rte_lpm_lookup uses only 128bit
since it is only uses SSE extension). 

>
>> +	while (n_left_from >= 4) {
>> +		/* Prefetch next-next mbufs */
>> +		if (likely(n_left_from >= 11)) {
>> +			rte_prefetch0(pkts[8]);
>> +			rte_prefetch0(pkts[9]);
>> +			rte_prefetch0(pkts[10]);
>> +			rte_prefetch0(pkts[11]);
>> +		}
>> +
>> +		/* Prefetch next mbuf data */
>> +		if (likely(n_left_from >= 7)) {
>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>> +						       struct rte_ether_hdr
>*) +
>> +				      1);
>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>> +						       struct rte_ether_hdr
>*) +
>> +				      1);
>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>> +						       struct rte_ether_hdr
>*) +
>> +				      1);
>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>> +						       struct rte_ether_hdr
>*) +
>> +				      1);
>> +		}
>> +
>> +		mbuf0 = pkts[0];
>> +		mbuf1 = pkts[1];
>> +		mbuf2 = pkts[2];
>> +		mbuf3 = pkts[3];
>> +
>> +		pkts += 4;
>> +		n_left_from -= 4;
>> +
>> +		/* Extract DIP of mbuf0 */
>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>rte_ether_hdr *);
>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>> +		ip0 = ipv4_hdr->dst_addr;
>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>hdr_checksum;
>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>time_to_live;
>> +
>> +		/* Extract DIP of mbuf1 */
>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>rte_ether_hdr *);
>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>> +		ip1 = ipv4_hdr->dst_addr;
>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>> +		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr-
>>hdr_checksum;
>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>time_to_live;
>> +
>> +		/* Extract DIP of mbuf2 */
>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>rte_ether_hdr *);
>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>> +		ip2 = ipv4_hdr->dst_addr;
>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>> +		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr-
>>hdr_checksum;
>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>time_to_live;
>> +
>> +		/* Extract DIP of mbuf3 */
>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>rte_ether_hdr *);
>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>> +		ip3 = ipv4_hdr->dst_addr;
>> +
>> +		/* Prepare for lookup x4 */
>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>> +
>> +		/* Byte swap 4 IPV4 addresses. */
>> +		const __m128i bswap_mask = _mm_set_epi8(
>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>> +
>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>> +		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr-
>>hdr_checksum;
>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>time_to_live;
>> +
>> +		/* Perform LPM lookup to get NH and next node */
>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>> +
>> +		/* Extract next node id and NH */
>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] &
>0xFFFF;
>> +		next0 = (dst.u32[0] >> 16);
>> +
>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] &
>0xFFFF;
>> +		next1 = (dst.u32[1] >> 16);
>> +
>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] &
>0xFFFF;
>> +		next2 = (dst.u32[2] >> 16);
>> +
>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] &
>0xFFFF;
>> +		next3 = (dst.u32[3] >> 16);
>> +
>> +		/* Enqueue four to next node */
>> +		rte_edge_t fix_spec =
>> +			(next_index ^ next0) | (next_index ^ next1) |
>> +			(next_index ^ next2) | (next_index ^ next3);
>> +
>> +		if (unlikely(fix_spec)) {
>> +			/* Copy things successfully speculated till now
>*/
>> +			rte_memcpy(to_next, from, last_spec *
>sizeof(from[0]));
>> +			from += last_spec;
>> +			to_next += last_spec;
>> +			held += last_spec;
>> +			last_spec = 0;
>> +
>> +			/* Next0 */
>> +			if (next_index == next0) {
>> +				to_next[0] = from[0];
>> +				to_next++;
>> +				held++;
>> +			} else {
>> +				rte_node_enqueue_x1(graph, node,
>next0,
>> +						    from[0]);
>> +			}
>> +
>> +			/* Next1 */
>> +			if (next_index == next1) {
>> +				to_next[0] = from[1];
>> +				to_next++;
>> +				held++;
>> +			} else {
>> +				rte_node_enqueue_x1(graph, node,
>next1,
>> +						    from[1]);
>> +			}
>> +
>> +			/* Next2 */
>> +			if (next_index == next2) {
>> +				to_next[0] = from[2];
>> +				to_next++;
>> +				held++;
>> +			} else {
>> +				rte_node_enqueue_x1(graph, node,
>next2,
>> +						    from[2]);
>> +			}
>> +
>> +			/* Next3 */
>> +			if (next_index == next3) {
>> +				to_next[0] = from[3];
>> +				to_next++;
>> +				held++;
>> +			} else {
>> +				rte_node_enqueue_x1(graph, node,
>next3,
>> +						    from[3]);
>> +			}
>> +
>> +			from += 4;
>> +
>> +		} else {
>> +			last_spec += 4;
>> +		}
>> +	}
>> +
>> +	while (n_left_from > 0) {
>> +		uint32_t next_hop;
>> +
>> +		mbuf0 = pkts[0];
>> +
>> +		pkts += 1;
>> +		n_left_from -= 1;
>> +
>> +		/* Extract DIP of mbuf0 */
>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>rte_ether_hdr *);
>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>hdr_checksum;
>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>time_to_live;
>> +
>> +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr-
>>dst_addr),
>> +				    &next_hop);
>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>> +
>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop &
>0xFFFF;
>> +		next0 = (next_hop >> 16);
>> +
>> +		if (unlikely(next_index ^ next0)) {
>> +			/* Copy things successfully speculated till now
>*/
>> +			rte_memcpy(to_next, from, last_spec *
>sizeof(from[0]));
>> +			from += last_spec;
>> +			to_next += last_spec;
>> +			held += last_spec;
>> +			last_spec = 0;
>> +
>> +			rte_node_enqueue_x1(graph, node, next0,
>from[0]);
>> +			from += 1;
>> +		} else {
>> +			last_spec += 1;
>> +		}
>> +	}
>> +
>> +	/* !!! Home run !!! */
>> +	if (likely(last_spec == nb_objs)) {
>> +		rte_node_next_stream_move(graph, node,
>next_index);
>> +		return nb_objs;
>> +	}
>> +
>> +	held += last_spec;
>> +	/* Copy things successfully speculated till now */
>> +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
>> +	rte_node_next_stream_put(graph, node, next_index, held);
>> +
>> +	return nb_objs;
>> +}
>> +
>>  #else
>>
>>  static uint16_t
>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-19 14:22     ` [dpdk-dev] [EXT] " Pavan Nikhilesh Bhagavatula
@ 2020-03-19 15:50       ` Ray Kinsella
  2020-03-19 16:13         ` Pavan Nikhilesh Bhagavatula
  0 siblings, 1 reply; 219+ messages in thread
From: Ray Kinsella @ 2020-03-19 15:50 UTC (permalink / raw)
  To: Pavan Nikhilesh Bhagavatula, Jerin Jacob Kollanukkaran,
	Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda



On 19/03/2020 14:22, Pavan Nikhilesh Bhagavatula wrote:
>> On 18/03/2020 21:35, jerinj@marvell.com wrote:
>>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>
>>> Add IPv4 lookup process function for ip4_lookup
>>> rte_node. This node performs LPM lookup using x86_64
>>> vector supported RTE_LPM API on every packet received
>>> and forwards it to a next node that is identified by
>>> lookup result.
>>>
>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>> ---
>>>  lib/librte_node/ip4_lookup.c | 245
>> +++++++++++++++++++++++++++++++++++
>>>  1 file changed, 245 insertions(+)
>>>
>>> diff --git a/lib/librte_node/ip4_lookup.c
>> b/lib/librte_node/ip4_lookup.c
>>> index d7fcd1158..c003e9c91 100644
>>> --- a/lib/librte_node/ip4_lookup.c
>>> +++ b/lib/librte_node/ip4_lookup.c
>>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct rte_graph
>> *graph, struct rte_node *node,
>>>  	return nb_objs;
>>>  }
>>>
>>> +#elif defined(RTE_ARCH_X86)
>>> +
>>> +/* X86 SSE */
>>> +static uint16_t
>>> +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node
>> *node,
>>> +			void **objs, uint16_t nb_objs)
>>> +{
>>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
>>> +	rte_edge_t next0, next1, next2, next3, next_index;
>>> +	struct rte_ipv4_hdr *ipv4_hdr;
>>> +	struct rte_ether_hdr *eth_hdr;
>>> +	uint32_t ip0, ip1, ip2, ip3;
>>> +	void **to_next, **from;
>>> +	uint16_t last_spec = 0;
>>> +	uint16_t n_left_from;
>>> +	struct rte_lpm *lpm;
>>> +	uint16_t held = 0;
>>> +	uint32_t drop_nh;
>>> +	rte_xmm_t dst;
>>> +	__m128i dip; /* SSE register */
>>> +	int rc, i;
>>> +
>>> +	/* Speculative next */
>>> +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>>> +	/* Drop node */
>>> +	drop_nh =
>> ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>>> +
>>> +	/* Get socket specific LPM from ctx */
>>> +	lpm = *((struct rte_lpm **)node->ctx);
>>> +
>>> +	pkts = (struct rte_mbuf **)objs;
>>> +	from = objs;
>>> +	n_left_from = nb_objs;
>>
>> I doubt this initial prefetch of the first 4 packets has any benefit.
> 
> Ack will remove in v2 for x86.
> 
>>
>>> +	if (n_left_from >= 4) {
>>> +		for (i = 0; i < 4; i++) {
>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>>> +						       struct rte_ether_hdr
>> *) +
>>> +				      1);
>>> +		}
>>> +	}
>>> +
>>> +	/* Get stream for the speculated next node */
>>> +	to_next = rte_node_next_stream_get(graph, node,
>> next_index, nb_objs);
>>
>> Suggest you don't reuse the hand-unrolling optimization from FD.io
>> VPP.
>> I have never found any performance benefit from them, and they
>> make the code unnecessarily verbose.
>>
> 
> How would be take the benefit of rte_lpm_lookupx4 without unrolling the loop?.
> Also, in future if we are using rte_rib and fib with a CPU supporting wider SIMD we might
> need to unroll them further (AVX256 AND 512 currently rte_lpm_lookup uses only 128bit
> since it is only uses SSE extension). 

Let the compiler do it for you, but using a constant vector length.
for (int i=0; i < 4; ++i) { ... }

> 
>>
>>> +	while (n_left_from >= 4) {
>>> +		/* Prefetch next-next mbufs */
>>> +		if (likely(n_left_from >= 11)) {
>>> +			rte_prefetch0(pkts[8]);
>>> +			rte_prefetch0(pkts[9]);
>>> +			rte_prefetch0(pkts[10]);
>>> +			rte_prefetch0(pkts[11]);
>>> +		}
>>> +
>>> +		/* Prefetch next mbuf data */
>>> +		if (likely(n_left_from >= 7)) {
>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>>> +						       struct rte_ether_hdr
>> *) +
>>> +				      1);
>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>>> +						       struct rte_ether_hdr
>> *) +
>>> +				      1);
>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>>> +						       struct rte_ether_hdr
>> *) +
>>> +				      1);
>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>>> +						       struct rte_ether_hdr
>> *) +
>>> +				      1);
>>> +		}
>>> +
>>> +		mbuf0 = pkts[0];
>>> +		mbuf1 = pkts[1];
>>> +		mbuf2 = pkts[2];
>>> +		mbuf3 = pkts[3];
>>> +
>>> +		pkts += 4;
>>> +		n_left_from -= 4;
>>> +
>>> +		/* Extract DIP of mbuf0 */
>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>> rte_ether_hdr *);
>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>> +		ip0 = ipv4_hdr->dst_addr;
>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>> hdr_checksum;
>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>> time_to_live;
>>> +
>>> +		/* Extract DIP of mbuf1 */
>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>> rte_ether_hdr *);
>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>> +		ip1 = ipv4_hdr->dst_addr;
>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>> +		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr-
>>> hdr_checksum;
>>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>> time_to_live;
>>> +
>>> +		/* Extract DIP of mbuf2 */
>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>> rte_ether_hdr *);
>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>> +		ip2 = ipv4_hdr->dst_addr;
>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>> +		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr-
>>> hdr_checksum;
>>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>> time_to_live;
>>> +
>>> +		/* Extract DIP of mbuf3 */
>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>> rte_ether_hdr *);
>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>> +		ip3 = ipv4_hdr->dst_addr;
>>> +
>>> +		/* Prepare for lookup x4 */
>>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>>> +
>>> +		/* Byte swap 4 IPV4 addresses. */
>>> +		const __m128i bswap_mask = _mm_set_epi8(
>>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
>>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>>> +
>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>> +		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr-
>>> hdr_checksum;
>>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>> time_to_live;
>>> +
>>> +		/* Perform LPM lookup to get NH and next node */
>>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>>> +
>>> +		/* Extract next node id and NH */
>>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] &
>> 0xFFFF;
>>> +		next0 = (dst.u32[0] >> 16);
>>> +
>>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] &
>> 0xFFFF;
>>> +		next1 = (dst.u32[1] >> 16);
>>> +
>>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] &
>> 0xFFFF;
>>> +		next2 = (dst.u32[2] >> 16);
>>> +
>>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] &
>> 0xFFFF;
>>> +		next3 = (dst.u32[3] >> 16);
>>> +
>>> +		/* Enqueue four to next node */
>>> +		rte_edge_t fix_spec =
>>> +			(next_index ^ next0) | (next_index ^ next1) |
>>> +			(next_index ^ next2) | (next_index ^ next3);
>>> +
>>> +		if (unlikely(fix_spec)) {
>>> +			/* Copy things successfully speculated till now
>> */
>>> +			rte_memcpy(to_next, from, last_spec *
>> sizeof(from[0]));
>>> +			from += last_spec;
>>> +			to_next += last_spec;
>>> +			held += last_spec;
>>> +			last_spec = 0;
>>> +
>>> +			/* Next0 */
>>> +			if (next_index == next0) {
>>> +				to_next[0] = from[0];
>>> +				to_next++;
>>> +				held++;
>>> +			} else {
>>> +				rte_node_enqueue_x1(graph, node,
>> next0,
>>> +						    from[0]);
>>> +			}
>>> +
>>> +			/* Next1 */
>>> +			if (next_index == next1) {
>>> +				to_next[0] = from[1];
>>> +				to_next++;
>>> +				held++;
>>> +			} else {
>>> +				rte_node_enqueue_x1(graph, node,
>> next1,
>>> +						    from[1]);
>>> +			}
>>> +
>>> +			/* Next2 */
>>> +			if (next_index == next2) {
>>> +				to_next[0] = from[2];
>>> +				to_next++;
>>> +				held++;
>>> +			} else {
>>> +				rte_node_enqueue_x1(graph, node,
>> next2,
>>> +						    from[2]);
>>> +			}
>>> +
>>> +			/* Next3 */
>>> +			if (next_index == next3) {
>>> +				to_next[0] = from[3];
>>> +				to_next++;
>>> +				held++;
>>> +			} else {
>>> +				rte_node_enqueue_x1(graph, node,
>> next3,
>>> +						    from[3]);
>>> +			}
>>> +
>>> +			from += 4;
>>> +
>>> +		} else {
>>> +			last_spec += 4;
>>> +		}
>>> +	}
>>> +
>>> +	while (n_left_from > 0) {
>>> +		uint32_t next_hop;
>>> +
>>> +		mbuf0 = pkts[0];
>>> +
>>> +		pkts += 1;
>>> +		n_left_from -= 1;
>>> +
>>> +		/* Extract DIP of mbuf0 */
>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>> rte_ether_hdr *);
>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>> hdr_checksum;
>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>> time_to_live;
>>> +
>>> +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr-
>>> dst_addr),
>>> +				    &next_hop);
>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>> +
>>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop &
>> 0xFFFF;
>>> +		next0 = (next_hop >> 16);
>>> +
>>> +		if (unlikely(next_index ^ next0)) {
>>> +			/* Copy things successfully speculated till now
>> */
>>> +			rte_memcpy(to_next, from, last_spec *
>> sizeof(from[0]));
>>> +			from += last_spec;
>>> +			to_next += last_spec;
>>> +			held += last_spec;
>>> +			last_spec = 0;
>>> +
>>> +			rte_node_enqueue_x1(graph, node, next0,
>> from[0]);
>>> +			from += 1;
>>> +		} else {
>>> +			last_spec += 1;
>>> +		}
>>> +	}
>>> +
>>> +	/* !!! Home run !!! */
>>> +	if (likely(last_spec == nb_objs)) {
>>> +		rte_node_next_stream_move(graph, node,
>> next_index);
>>> +		return nb_objs;
>>> +	}
>>> +
>>> +	held += last_spec;
>>> +	/* Copy things successfully speculated till now */
>>> +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
>>> +	rte_node_next_stream_put(graph, node, next_index, held);
>>> +
>>> +	return nb_objs;
>>> +}
>>> +
>>>  #else
>>>
>>>  static uint16_t
>>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-19 15:50       ` Ray Kinsella
@ 2020-03-19 16:13         ` Pavan Nikhilesh Bhagavatula
  2020-03-20  9:14           ` Ray Kinsella
  0 siblings, 1 reply; 219+ messages in thread
From: Pavan Nikhilesh Bhagavatula @ 2020-03-19 16:13 UTC (permalink / raw)
  To: Ray Kinsella, Jerin Jacob Kollanukkaran, Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda



>-----Original Message-----
>From: Ray Kinsella <mdr@ashroe.eu>
>Sent: Thursday, March 19, 2020 9:21 PM
>To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>; Jerin
>Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar Dabilpuram
><ndabilpuram@marvell.com>
>Cc: dev@dpdk.org; thomas@monjalon.net;
>david.marchand@redhat.com; mattias.ronnblom@ericsson.com; Kiran
>Kumar Kokkilagadda <kirankumark@marvell.com>
>Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup
>for x86
>
>
>
>On 19/03/2020 14:22, Pavan Nikhilesh Bhagavatula wrote:
>>> On 18/03/2020 21:35, jerinj@marvell.com wrote:
>>>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>
>>>> Add IPv4 lookup process function for ip4_lookup
>>>> rte_node. This node performs LPM lookup using x86_64
>>>> vector supported RTE_LPM API on every packet received
>>>> and forwards it to a next node that is identified by
>>>> lookup result.
>>>>
>>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
>>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>>> ---
>>>>  lib/librte_node/ip4_lookup.c | 245
>>> +++++++++++++++++++++++++++++++++++
>>>>  1 file changed, 245 insertions(+)
>>>>
>>>> diff --git a/lib/librte_node/ip4_lookup.c
>>> b/lib/librte_node/ip4_lookup.c
>>>> index d7fcd1158..c003e9c91 100644
>>>> --- a/lib/librte_node/ip4_lookup.c
>>>> +++ b/lib/librte_node/ip4_lookup.c
>>>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct
>rte_graph
>>> *graph, struct rte_node *node,
>>>>  	return nb_objs;
>>>>  }
>>>>
>>>> +#elif defined(RTE_ARCH_X86)
>>>> +
>>>> +/* X86 SSE */
>>>> +static uint16_t
>>>> +ip4_lookup_node_process(struct rte_graph *graph, struct
>rte_node
>>> *node,
>>>> +			void **objs, uint16_t nb_objs)
>>>> +{
>>>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
>>>> +	rte_edge_t next0, next1, next2, next3, next_index;
>>>> +	struct rte_ipv4_hdr *ipv4_hdr;
>>>> +	struct rte_ether_hdr *eth_hdr;
>>>> +	uint32_t ip0, ip1, ip2, ip3;
>>>> +	void **to_next, **from;
>>>> +	uint16_t last_spec = 0;
>>>> +	uint16_t n_left_from;
>>>> +	struct rte_lpm *lpm;
>>>> +	uint16_t held = 0;
>>>> +	uint32_t drop_nh;
>>>> +	rte_xmm_t dst;
>>>> +	__m128i dip; /* SSE register */
>>>> +	int rc, i;
>>>> +
>>>> +	/* Speculative next */
>>>> +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>>>> +	/* Drop node */
>>>> +	drop_nh =
>>> ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>>>> +
>>>> +	/* Get socket specific LPM from ctx */
>>>> +	lpm = *((struct rte_lpm **)node->ctx);
>>>> +
>>>> +	pkts = (struct rte_mbuf **)objs;
>>>> +	from = objs;
>>>> +	n_left_from = nb_objs;
>>>
>>> I doubt this initial prefetch of the first 4 packets has any benefit.
>>
>> Ack will remove in v2 for x86.
>>
>>>
>>>> +	if (n_left_from >= 4) {
>>>> +		for (i = 0; i < 4; i++) {
>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>>>> +						       struct rte_ether_hdr
>>> *) +
>>>> +				      1);
>>>> +		}
>>>> +	}
>>>> +
>>>> +	/* Get stream for the speculated next node */
>>>> +	to_next = rte_node_next_stream_get(graph, node,
>>> next_index, nb_objs);
>>>
>>> Suggest you don't reuse the hand-unrolling optimization from FD.io
>>> VPP.
>>> I have never found any performance benefit from them, and they
>>> make the code unnecessarily verbose.
>>>
>>
>> How would be take the benefit of rte_lpm_lookupx4 without
>unrolling the loop?.
>> Also, in future if we are using rte_rib and fib with a CPU supporting
>wider SIMD we might
>> need to unroll them further (AVX256 AND 512 currently
>rte_lpm_lookup uses only 128bit
>> since it is only uses SSE extension).
>
>Let the compiler do it for you, but using a constant vector length.
>for (int i=0; i < 4; ++i) { ... }
>

Ok, I think I misunderstood the previous comment. 
It was only for the prefetches in the loop right?

>>
>>>
>>>> +	while (n_left_from >= 4) {
>>>> +		/* Prefetch next-next mbufs */
>>>> +		if (likely(n_left_from >= 11)) {
>>>> +			rte_prefetch0(pkts[8]);
>>>> +			rte_prefetch0(pkts[9]);
>>>> +			rte_prefetch0(pkts[10]);
>>>> +			rte_prefetch0(pkts[11]);
>>>> +		}
>>>> +
>>>> +		/* Prefetch next mbuf data */
>>>> +		if (likely(n_left_from >= 7)) {
>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>>>> +						       struct rte_ether_hdr
>>> *) +
>>>> +				      1);
>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>>>> +						       struct rte_ether_hdr
>>> *) +
>>>> +				      1);
>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>>>> +						       struct rte_ether_hdr
>>> *) +
>>>> +				      1);
>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>>>> +						       struct rte_ether_hdr
>>> *) +
>>>> +				      1);
>>>> +		}
>>>> +
>>>> +		mbuf0 = pkts[0];
>>>> +		mbuf1 = pkts[1];
>>>> +		mbuf2 = pkts[2];
>>>> +		mbuf3 = pkts[3];
>>>> +
>>>> +		pkts += 4;
>>>> +		n_left_from -= 4;
>>>> +
>>>> +		/* Extract DIP of mbuf0 */
>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>> rte_ether_hdr *);
>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>> +		ip0 = ipv4_hdr->dst_addr;
>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>>> hdr_checksum;
>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>> time_to_live;
>>>> +
>>>> +		/* Extract DIP of mbuf1 */
>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>>> rte_ether_hdr *);
>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>> +		ip1 = ipv4_hdr->dst_addr;
>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>> +		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr-
>>>> hdr_checksum;
>>>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>>> time_to_live;
>>>> +
>>>> +		/* Extract DIP of mbuf2 */
>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>>> rte_ether_hdr *);
>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>> +		ip2 = ipv4_hdr->dst_addr;
>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>> +		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr-
>>>> hdr_checksum;
>>>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>>> time_to_live;
>>>> +
>>>> +		/* Extract DIP of mbuf3 */
>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>>> rte_ether_hdr *);
>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>> +		ip3 = ipv4_hdr->dst_addr;
>>>> +
>>>> +		/* Prepare for lookup x4 */
>>>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>>>> +
>>>> +		/* Byte swap 4 IPV4 addresses. */
>>>> +		const __m128i bswap_mask = _mm_set_epi8(
>>>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
>>>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>>>> +
>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>> +		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr-
>>>> hdr_checksum;
>>>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>>> time_to_live;
>>>> +
>>>> +		/* Perform LPM lookup to get NH and next node */
>>>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>>>> +
>>>> +		/* Extract next node id and NH */
>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] &
>>> 0xFFFF;
>>>> +		next0 = (dst.u32[0] >> 16);
>>>> +
>>>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] &
>>> 0xFFFF;
>>>> +		next1 = (dst.u32[1] >> 16);
>>>> +
>>>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] &
>>> 0xFFFF;
>>>> +		next2 = (dst.u32[2] >> 16);
>>>> +
>>>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] &
>>> 0xFFFF;
>>>> +		next3 = (dst.u32[3] >> 16);
>>>> +
>>>> +		/* Enqueue four to next node */
>>>> +		rte_edge_t fix_spec =
>>>> +			(next_index ^ next0) | (next_index ^ next1) |
>>>> +			(next_index ^ next2) | (next_index ^ next3);
>>>> +
>>>> +		if (unlikely(fix_spec)) {
>>>> +			/* Copy things successfully speculated till now
>>> */
>>>> +			rte_memcpy(to_next, from, last_spec *
>>> sizeof(from[0]));
>>>> +			from += last_spec;
>>>> +			to_next += last_spec;
>>>> +			held += last_spec;
>>>> +			last_spec = 0;
>>>> +
>>>> +			/* Next0 */
>>>> +			if (next_index == next0) {
>>>> +				to_next[0] = from[0];
>>>> +				to_next++;
>>>> +				held++;
>>>> +			} else {
>>>> +				rte_node_enqueue_x1(graph, node,
>>> next0,
>>>> +						    from[0]);
>>>> +			}
>>>> +
>>>> +			/* Next1 */
>>>> +			if (next_index == next1) {
>>>> +				to_next[0] = from[1];
>>>> +				to_next++;
>>>> +				held++;
>>>> +			} else {
>>>> +				rte_node_enqueue_x1(graph, node,
>>> next1,
>>>> +						    from[1]);
>>>> +			}
>>>> +
>>>> +			/* Next2 */
>>>> +			if (next_index == next2) {
>>>> +				to_next[0] = from[2];
>>>> +				to_next++;
>>>> +				held++;
>>>> +			} else {
>>>> +				rte_node_enqueue_x1(graph, node,
>>> next2,
>>>> +						    from[2]);
>>>> +			}
>>>> +
>>>> +			/* Next3 */
>>>> +			if (next_index == next3) {
>>>> +				to_next[0] = from[3];
>>>> +				to_next++;
>>>> +				held++;
>>>> +			} else {
>>>> +				rte_node_enqueue_x1(graph, node,
>>> next3,
>>>> +						    from[3]);
>>>> +			}
>>>> +
>>>> +			from += 4;
>>>> +
>>>> +		} else {
>>>> +			last_spec += 4;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	while (n_left_from > 0) {
>>>> +		uint32_t next_hop;
>>>> +
>>>> +		mbuf0 = pkts[0];
>>>> +
>>>> +		pkts += 1;
>>>> +		n_left_from -= 1;
>>>> +
>>>> +		/* Extract DIP of mbuf0 */
>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>> rte_ether_hdr *);
>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>>> hdr_checksum;
>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>> time_to_live;
>>>> +
>>>> +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr-
>>>> dst_addr),
>>>> +				    &next_hop);
>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>> +
>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop &
>>> 0xFFFF;
>>>> +		next0 = (next_hop >> 16);
>>>> +
>>>> +		if (unlikely(next_index ^ next0)) {
>>>> +			/* Copy things successfully speculated till now
>>> */
>>>> +			rte_memcpy(to_next, from, last_spec *
>>> sizeof(from[0]));
>>>> +			from += last_spec;
>>>> +			to_next += last_spec;
>>>> +			held += last_spec;
>>>> +			last_spec = 0;
>>>> +
>>>> +			rte_node_enqueue_x1(graph, node, next0,
>>> from[0]);
>>>> +			from += 1;
>>>> +		} else {
>>>> +			last_spec += 1;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	/* !!! Home run !!! */
>>>> +	if (likely(last_spec == nb_objs)) {
>>>> +		rte_node_next_stream_move(graph, node,
>>> next_index);
>>>> +		return nb_objs;
>>>> +	}
>>>> +
>>>> +	held += last_spec;
>>>> +	/* Copy things successfully speculated till now */
>>>> +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
>>>> +	rte_node_next_stream_put(graph, node, next_index, held);
>>>> +
>>>> +	return nb_objs;
>>>> +}
>>>> +
>>>>  #else
>>>>
>>>>  static uint16_t
>>>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-19 16:13         ` Pavan Nikhilesh Bhagavatula
@ 2020-03-20  9:14           ` Ray Kinsella
  2020-03-24  9:40             ` Pavan Nikhilesh Bhagavatula
  0 siblings, 1 reply; 219+ messages in thread
From: Ray Kinsella @ 2020-03-20  9:14 UTC (permalink / raw)
  To: Pavan Nikhilesh Bhagavatula, Jerin Jacob Kollanukkaran,
	Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda



On 19/03/2020 16:13, Pavan Nikhilesh Bhagavatula wrote:
> 
> 
>> -----Original Message-----
>> From: Ray Kinsella <mdr@ashroe.eu>
>> Sent: Thursday, March 19, 2020 9:21 PM
>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>; Jerin
>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar Dabilpuram
>> <ndabilpuram@marvell.com>
>> Cc: dev@dpdk.org; thomas@monjalon.net;
>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com; Kiran
>> Kumar Kokkilagadda <kirankumark@marvell.com>
>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup
>> for x86
>>
>>
>>
>> On 19/03/2020 14:22, Pavan Nikhilesh Bhagavatula wrote:
>>>> On 18/03/2020 21:35, jerinj@marvell.com wrote:
>>>>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>
>>>>> Add IPv4 lookup process function for ip4_lookup
>>>>> rte_node. This node performs LPM lookup using x86_64
>>>>> vector supported RTE_LPM API on every packet received
>>>>> and forwards it to a next node that is identified by
>>>>> lookup result.
>>>>>
>>>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
>>>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>>>> ---
>>>>>  lib/librte_node/ip4_lookup.c | 245
>>>> +++++++++++++++++++++++++++++++++++
>>>>>  1 file changed, 245 insertions(+)
>>>>>
>>>>> diff --git a/lib/librte_node/ip4_lookup.c
>>>> b/lib/librte_node/ip4_lookup.c
>>>>> index d7fcd1158..c003e9c91 100644
>>>>> --- a/lib/librte_node/ip4_lookup.c
>>>>> +++ b/lib/librte_node/ip4_lookup.c
>>>>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct
>> rte_graph
>>>> *graph, struct rte_node *node,
>>>>>  	return nb_objs;
>>>>>  }
>>>>>
>>>>> +#elif defined(RTE_ARCH_X86)
>>>>> +
>>>>> +/* X86 SSE */
>>>>> +static uint16_t
>>>>> +ip4_lookup_node_process(struct rte_graph *graph, struct
>> rte_node
>>>> *node,
>>>>> +			void **objs, uint16_t nb_objs)
>>>>> +{
>>>>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
>>>>> +	rte_edge_t next0, next1, next2, next3, next_index;
>>>>> +	struct rte_ipv4_hdr *ipv4_hdr;
>>>>> +	struct rte_ether_hdr *eth_hdr;
>>>>> +	uint32_t ip0, ip1, ip2, ip3;
>>>>> +	void **to_next, **from;
>>>>> +	uint16_t last_spec = 0;
>>>>> +	uint16_t n_left_from;
>>>>> +	struct rte_lpm *lpm;
>>>>> +	uint16_t held = 0;
>>>>> +	uint32_t drop_nh;
>>>>> +	rte_xmm_t dst;
>>>>> +	__m128i dip; /* SSE register */
>>>>> +	int rc, i;
>>>>> +
>>>>> +	/* Speculative next */
>>>>> +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>>>>> +	/* Drop node */
>>>>> +	drop_nh =
>>>> ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>>>>> +
>>>>> +	/* Get socket specific LPM from ctx */
>>>>> +	lpm = *((struct rte_lpm **)node->ctx);
>>>>> +
>>>>> +	pkts = (struct rte_mbuf **)objs;
>>>>> +	from = objs;
>>>>> +	n_left_from = nb_objs;
>>>>
>>>> I doubt this initial prefetch of the first 4 packets has any benefit.
>>>
>>> Ack will remove in v2 for x86.
>>>
>>>>
>>>>> +	if (n_left_from >= 4) {
>>>>> +		for (i = 0; i < 4; i++) {
>>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>>>>> +						       struct rte_ether_hdr
>>>> *) +
>>>>> +				      1);
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	/* Get stream for the speculated next node */
>>>>> +	to_next = rte_node_next_stream_get(graph, node,
>>>> next_index, nb_objs);
>>>>
>>>> Suggest you don't reuse the hand-unrolling optimization from FD.io
>>>> VPP.
>>>> I have never found any performance benefit from them, and they
>>>> make the code unnecessarily verbose.
>>>>
>>>
>>> How would be take the benefit of rte_lpm_lookupx4 without
>> unrolling the loop?.
>>> Also, in future if we are using rte_rib and fib with a CPU supporting
>> wider SIMD we might
>>> need to unroll them further (AVX256 AND 512 currently
>> rte_lpm_lookup uses only 128bit
>>> since it is only uses SSE extension).
>>
>> Let the compiler do it for you, but using a constant vector length.
>> for (int i=0; i < 4; ++i) { ... }
>>
> 
> Ok, I think I misunderstood the previous comment. 
> It was only for the prefetches in the loop right?


no, it was for all the needless repetition.
hand-unrolling loops serve no purpose but to add verbosity. 

> 
>>>
>>>>
>>>>> +	while (n_left_from >= 4) {
>>>>> +		/* Prefetch next-next mbufs */
>>>>> +		if (likely(n_left_from >= 11)) {
>>>>> +			rte_prefetch0(pkts[8]);
>>>>> +			rte_prefetch0(pkts[9]);
>>>>> +			rte_prefetch0(pkts[10]);
>>>>> +			rte_prefetch0(pkts[11]);
>>>>> +		}
>>>>> +
>>>>> +		/* Prefetch next mbuf data */
>>>>> +		if (likely(n_left_from >= 7)) {
>>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>>>>> +						       struct rte_ether_hdr
>>>> *) +
>>>>> +				      1);
>>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>>>>> +						       struct rte_ether_hdr
>>>> *) +
>>>>> +				      1);
>>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>>>>> +						       struct rte_ether_hdr
>>>> *) +
>>>>> +				      1);
>>>>> +			rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>>>>> +						       struct rte_ether_hdr
>>>> *) +
>>>>> +				      1);
>>>>> +		}
>>>>> +
>>>>> +		mbuf0 = pkts[0];
>>>>> +		mbuf1 = pkts[1];
>>>>> +		mbuf2 = pkts[2];
>>>>> +		mbuf3 = pkts[3];
>>>>> +
>>>>> +		pkts += 4;
>>>>> +		n_left_from -= 4;
>>>>> +
>>>>> +		/* Extract DIP of mbuf0 */
>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>> rte_ether_hdr *);
>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>> +		ip0 = ipv4_hdr->dst_addr;
>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>>>> hdr_checksum;
>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>> time_to_live;
>>>>> +
>>>>> +		/* Extract DIP of mbuf1 */
>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>>>> rte_ether_hdr *);
>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>> +		ip1 = ipv4_hdr->dst_addr;
>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>> +		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr-
>>>>> hdr_checksum;
>>>>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>>>> time_to_live;
>>>>> +
>>>>> +		/* Extract DIP of mbuf2 */
>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>>>> rte_ether_hdr *);
>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>> +		ip2 = ipv4_hdr->dst_addr;
>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>> +		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr-
>>>>> hdr_checksum;
>>>>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>>>> time_to_live;
>>>>> +
>>>>> +		/* Extract DIP of mbuf3 */
>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>>>> rte_ether_hdr *);
>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>> +		ip3 = ipv4_hdr->dst_addr;
>>>>> +
>>>>> +		/* Prepare for lookup x4 */
>>>>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>>>>> +
>>>>> +		/* Byte swap 4 IPV4 addresses. */
>>>>> +		const __m128i bswap_mask = _mm_set_epi8(
>>>>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
>>>>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>>>>> +
>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>> +		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr-
>>>>> hdr_checksum;
>>>>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>>>> time_to_live;
>>>>> +
>>>>> +		/* Perform LPM lookup to get NH and next node */
>>>>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>>>>> +
>>>>> +		/* Extract next node id and NH */
>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] &
>>>> 0xFFFF;
>>>>> +		next0 = (dst.u32[0] >> 16);
>>>>> +
>>>>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] &
>>>> 0xFFFF;
>>>>> +		next1 = (dst.u32[1] >> 16);
>>>>> +
>>>>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] &
>>>> 0xFFFF;
>>>>> +		next2 = (dst.u32[2] >> 16);
>>>>> +
>>>>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] &
>>>> 0xFFFF;
>>>>> +		next3 = (dst.u32[3] >> 16);
>>>>> +
>>>>> +		/* Enqueue four to next node */
>>>>> +		rte_edge_t fix_spec =
>>>>> +			(next_index ^ next0) | (next_index ^ next1) |
>>>>> +			(next_index ^ next2) | (next_index ^ next3);
>>>>> +
>>>>> +		if (unlikely(fix_spec)) {
>>>>> +			/* Copy things successfully speculated till now
>>>> */
>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>> sizeof(from[0]));
>>>>> +			from += last_spec;
>>>>> +			to_next += last_spec;
>>>>> +			held += last_spec;
>>>>> +			last_spec = 0;
>>>>> +
>>>>> +			/* Next0 */
>>>>> +			if (next_index == next0) {
>>>>> +				to_next[0] = from[0];
>>>>> +				to_next++;
>>>>> +				held++;
>>>>> +			} else {
>>>>> +				rte_node_enqueue_x1(graph, node,
>>>> next0,
>>>>> +						    from[0]);
>>>>> +			}
>>>>> +
>>>>> +			/* Next1 */
>>>>> +			if (next_index == next1) {
>>>>> +				to_next[0] = from[1];
>>>>> +				to_next++;
>>>>> +				held++;
>>>>> +			} else {
>>>>> +				rte_node_enqueue_x1(graph, node,
>>>> next1,
>>>>> +						    from[1]);
>>>>> +			}
>>>>> +
>>>>> +			/* Next2 */
>>>>> +			if (next_index == next2) {
>>>>> +				to_next[0] = from[2];
>>>>> +				to_next++;
>>>>> +				held++;
>>>>> +			} else {
>>>>> +				rte_node_enqueue_x1(graph, node,
>>>> next2,
>>>>> +						    from[2]);
>>>>> +			}
>>>>> +
>>>>> +			/* Next3 */
>>>>> +			if (next_index == next3) {
>>>>> +				to_next[0] = from[3];
>>>>> +				to_next++;
>>>>> +				held++;
>>>>> +			} else {
>>>>> +				rte_node_enqueue_x1(graph, node,
>>>> next3,
>>>>> +						    from[3]);
>>>>> +			}
>>>>> +
>>>>> +			from += 4;
>>>>> +
>>>>> +		} else {
>>>>> +			last_spec += 4;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	while (n_left_from > 0) {
>>>>> +		uint32_t next_hop;
>>>>> +
>>>>> +		mbuf0 = pkts[0];
>>>>> +
>>>>> +		pkts += 1;
>>>>> +		n_left_from -= 1;
>>>>> +
>>>>> +		/* Extract DIP of mbuf0 */
>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>> rte_ether_hdr *);
>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr-
>>>>> hdr_checksum;
>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>> time_to_live;
>>>>> +
>>>>> +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr-
>>>>> dst_addr),
>>>>> +				    &next_hop);
>>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>>> +
>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop &
>>>> 0xFFFF;
>>>>> +		next0 = (next_hop >> 16);
>>>>> +
>>>>> +		if (unlikely(next_index ^ next0)) {
>>>>> +			/* Copy things successfully speculated till now
>>>> */
>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>> sizeof(from[0]));
>>>>> +			from += last_spec;
>>>>> +			to_next += last_spec;
>>>>> +			held += last_spec;
>>>>> +			last_spec = 0;
>>>>> +
>>>>> +			rte_node_enqueue_x1(graph, node, next0,
>>>> from[0]);
>>>>> +			from += 1;
>>>>> +		} else {
>>>>> +			last_spec += 1;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	/* !!! Home run !!! */
>>>>> +	if (likely(last_spec == nb_objs)) {
>>>>> +		rte_node_next_stream_move(graph, node,
>>>> next_index);
>>>>> +		return nb_objs;
>>>>> +	}
>>>>> +
>>>>> +	held += last_spec;
>>>>> +	/* Copy things successfully speculated till now */
>>>>> +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
>>>>> +	rte_node_next_stream_put(graph, node, next_index, held);
>>>>> +
>>>>> +	return nb_objs;
>>>>> +}
>>>>> +
>>>>>  #else
>>>>>
>>>>>  static uint16_t
>>>>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-20  9:14           ` Ray Kinsella
@ 2020-03-24  9:40             ` Pavan Nikhilesh Bhagavatula
  2020-03-24 14:38               ` Ray Kinsella
  0 siblings, 1 reply; 219+ messages in thread
From: Pavan Nikhilesh Bhagavatula @ 2020-03-24  9:40 UTC (permalink / raw)
  To: Ray Kinsella, Jerin Jacob Kollanukkaran, Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda

Hi Ray, 

I have tried to avoid hand unrolling loops and found the following observations.

1. Although it decreases LOC it also takes away readability too.
	Example: 
	Avoiding unrolled code below

                priv[0].u64[0] = rte_node_mbuf_priv1(mbuf[0])->u;
                priv[0].u64[1] = rte_node_mbuf_priv1(mbuf[1])->u;
                priv[1].u64[0] = rte_node_mbuf_priv1(mbuf[2])->u;
                priv[1].u64[1] = rte_node_mbuf_priv1(mbuf[3])->u;

                /* Increment checksum by one. */
                priv[0].u32[1] += rte_cpu_to_be_16(0x0100);
                priv[0].u32[3] += rte_cpu_to_be_16(0x0100);
                priv[1].u32[1] += rte_cpu_to_be_16(0x0100);
                priv[1].u32[3] += rte_cpu_to_be_16(0x0100);

                d = rte_pktmbuf_mtod(mbuf[0], void *);
                rte_memcpy(d, nh[priv[0].u16[0]].rewrite_data,
                                nh[priv[0].u16[0]].rewrite_len);
                next[0] = nh[priv[0].u16[0]].tx_node;
                ip = (struct rte_ipv4_hdr *)((uint8_t *)d +
                                sizeof(struct rte_ether_hdr));
                ip->time_to_live = priv[0].u16[1] - 1;
                ip->hdr_checksum = priv[0].u16[2] + priv[0].u16[3];

                d = rte_pktmbuf_mtod(mbuf[1], void *);
                rte_memcpy(d, nh[priv[0].u16[4]].rewrite_data,
                           nh[priv[0].u16[4]].rewrite_len);
                next[1] = nh[priv[0].u16[4]].tx_node;
                ip = (struct rte_ipv4_hdr *)((uint8_t *)d +
                      sizeof(struct rte_ether_hdr));
                ip->time_to_live = priv[0].u16[5] - 1;
                ip->hdr_checksum = priv[0].u16[6] + priv[0].u16[7];

                d = rte_pktmbuf_mtod(mbuf[2], void *);
                rte_memcpy(d, nh[priv[1].u16[0]].rewrite_data,
                           nh[priv[1].u16[0]].rewrite_len);
                next[2] = nh[priv[1].u16[0]].tx_node;
                ip = (struct rte_ipv4_hdr *)((uint8_t *)d +
                      sizeof(struct rte_ether_hdr));
                ip->time_to_live = priv[1].u16[1] - 1;
                ip->hdr_checksum = priv[1].u16[2] + priv[1].u16[3];

                d = rte_pktmbuf_mtod(mbuf[3], void *);
                rte_memcpy(d, nh[priv[1].u16[4]].rewrite_data,
                           nh[priv[1].u16[4]].rewrite_len);
                next[3] = nh[priv[1].u16[4]].tx_node;
                ip = (struct rte_ipv4_hdr *)((uint8_t *)d +
                      sizeof(struct rte_ether_hdr));
                ip->time_to_live = priv[1].u16[5] - 1;
                ip->hdr_checksum = priv[1].u16[6] + priv[1].u16[7];

	Leads to something like:
	
                for (i = 0, j = 0; i < BUF_PER_LOOP; i += 2, j++) {
                        priv[j].u64[0] = rte_node_mbuf_priv1(mbuf[i])->u;
                        priv[j].u32[1] += rte_cpu_to_be_16(0x0100);
                        d = rte_pktmbuf_mtod(mbuf[i], void *);
                        ip = (struct rte_ipv4_hdr *)((uint8_t *)d +
                                            sizeof(struct rte_ether_hdr));
                        ip->time_to_live = priv[j].u16[1] - 1;
                        ip->hdr_checksum = priv[j].u16[2] + priv[j].u16[3];
                        rte_memcpy(d, nh[priv[j].u16[0]].rewrite_data,
                                   nh[priv[j].u16[0]].rewrite_len);

                        next[i] = nh[priv[j].u16[0]].tx_node;

                        priv[j].u64[1] = rte_node_mbuf_priv1(mbuf[i + 1])->u;
                        priv[j].u32[3] += rte_cpu_to_be_16(0x0100);
                        d = rte_pktmbuf_mtod(mbuf[i + 1], void *);
                        ip = (struct rte_ipv4_hdr *)((uint8_t *)d +
                                        sizeof(struct rte_ether_hdr));
                        ip->time_to_live = priv[j].u16[5] - 1;
                        ip->hdr_checksum = priv[j].u16[6] + priv[j].u16[7];
                        rte_memcpy(d, nh[priv[j].u16[4]].rewrite_data,
                                   nh[priv[j].u16[4]].rewrite_len);

                        next[i + 1] = nh[priv[j].u16[4]].tx_node;
                }
	Which is kind of unreadable.

2. Not all compilers are made equal. I found that most of the compilers don’t 
     Unroll the loop above even when compiled with `-funroll-all-loops`.
     I have checked with following compilers:
	GCC 9.2.0
	Clang 9.0.1
	Aarch64 GCC 7.3.0
	Aarch64 GCC 9.2.0
			

3. Performance wise I see a lot of degradation on our platform at least 13%.
    On IA with a Broadwell(Xeon E5-2690) and i40e the performance remain same w.r.t Rx/Tx since the 
    hotspot is in the Tx path of the driver which limits the per core capability.
    But the performance difference in number of cycles per node can be seen below:

	Hand unrolling:
+-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
|Node                           |calls          |objs           |realloc_count  |objs/call      |objs/sec(10E6) |cycles/call|
+-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
|ip4_lookup                     |7765918        |248509344      |1              |32.000         |27.725408      |779.0000   |
|ip4_rewrite                    |7765925        |248509568      |1              |32.000         |27.725408      |425.0000   |
|ethdev_tx-1                    |7765927        |204056223      |1              |26.000         |22.762720      |597.0000   |
|pkt_drop                       |1389170        |44453409       |1              |32.000         |4.962688       |298.0000   |
|ethdev_rx-0-0                  |63604111       |248509792      |2              |32.000         |27.725408      |982.0000   |
+-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
	
	W/o unrolling:

+-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
|Node                           |calls          |objs           |realloc_count  |objs/call      |objs/sec(10E6) |cycles/call|
+-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
|ip4_lookup                     |18864640       |603668448      |1              |32.000         |26.051328      |828.0000   |
|ip4_rewrite                    |18864646       |603668640      |1              |32.000         |26.051328      |534.0000   |
|ethdev_tx-1                    |18864648       |527874175      |1              |27.000         |22.780256      |633.0000   |
|pkt_drop                       |2368580        |75794529       |1              |32.000         |3.271072       |286.0000   |
|ethdev_rx-0-0                  |282058226      |603668864      |2              |32.000         |26.051328      |994.0000   |
+-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+

Considering the above findings we would like to continue unrolling the loops by hand.

Regards,
Pavan.

>-----Original Message-----
>From: Ray Kinsella <mdr@ashroe.eu>
>Sent: Friday, March 20, 2020 2:44 PM
>To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>; Jerin
>Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar Dabilpuram
><ndabilpuram@marvell.com>
>Cc: dev@dpdk.org; thomas@monjalon.net;
>david.marchand@redhat.com; mattias.ronnblom@ericsson.com; Kiran
>Kumar Kokkilagadda <kirankumark@marvell.com>
>Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup
>for x86
>
>
>
>On 19/03/2020 16:13, Pavan Nikhilesh Bhagavatula wrote:
>>
>>
>>> -----Original Message-----
>>> From: Ray Kinsella <mdr@ashroe.eu>
>>> Sent: Thursday, March 19, 2020 9:21 PM
>>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>;
>Jerin
>>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar
>Dabilpuram
>>> <ndabilpuram@marvell.com>
>>> Cc: dev@dpdk.org; thomas@monjalon.net;
>>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com;
>Kiran
>>> Kumar Kokkilagadda <kirankumark@marvell.com>
>>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4
>lookup
>>> for x86
>>>
>>>
>>>
>>> On 19/03/2020 14:22, Pavan Nikhilesh Bhagavatula wrote:
>>>>> On 18/03/2020 21:35, jerinj@marvell.com wrote:
>>>>>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>>
>>>>>> Add IPv4 lookup process function for ip4_lookup
>>>>>> rte_node. This node performs LPM lookup using x86_64
>>>>>> vector supported RTE_LPM API on every packet received
>>>>>> and forwards it to a next node that is identified by
>>>>>> lookup result.
>>>>>>
>>>>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
>>>>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>>>>> ---
>>>>>>  lib/librte_node/ip4_lookup.c | 245
>>>>> +++++++++++++++++++++++++++++++++++
>>>>>>  1 file changed, 245 insertions(+)
>>>>>>
>>>>>> diff --git a/lib/librte_node/ip4_lookup.c
>>>>> b/lib/librte_node/ip4_lookup.c
>>>>>> index d7fcd1158..c003e9c91 100644
>>>>>> --- a/lib/librte_node/ip4_lookup.c
>>>>>> +++ b/lib/librte_node/ip4_lookup.c
>>>>>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct
>>> rte_graph
>>>>> *graph, struct rte_node *node,
>>>>>>  	return nb_objs;
>>>>>>  }
>>>>>>
>>>>>> +#elif defined(RTE_ARCH_X86)
>>>>>> +
>>>>>> +/* X86 SSE */
>>>>>> +static uint16_t
>>>>>> +ip4_lookup_node_process(struct rte_graph *graph, struct
>>> rte_node
>>>>> *node,
>>>>>> +			void **objs, uint16_t nb_objs)
>>>>>> +{
>>>>>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3,
>**pkts;
>>>>>> +	rte_edge_t next0, next1, next2, next3, next_index;
>>>>>> +	struct rte_ipv4_hdr *ipv4_hdr;
>>>>>> +	struct rte_ether_hdr *eth_hdr;
>>>>>> +	uint32_t ip0, ip1, ip2, ip3;
>>>>>> +	void **to_next, **from;
>>>>>> +	uint16_t last_spec = 0;
>>>>>> +	uint16_t n_left_from;
>>>>>> +	struct rte_lpm *lpm;
>>>>>> +	uint16_t held = 0;
>>>>>> +	uint32_t drop_nh;
>>>>>> +	rte_xmm_t dst;
>>>>>> +	__m128i dip; /* SSE register */
>>>>>> +	int rc, i;
>>>>>> +
>>>>>> +	/* Speculative next */
>>>>>> +	next_index =
>RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>>>>>> +	/* Drop node */
>>>>>> +	drop_nh =
>>>>> ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>>>>>> +
>>>>>> +	/* Get socket specific LPM from ctx */
>>>>>> +	lpm = *((struct rte_lpm **)node->ctx);
>>>>>> +
>>>>>> +	pkts = (struct rte_mbuf **)objs;
>>>>>> +	from = objs;
>>>>>> +	n_left_from = nb_objs;
>>>>>
>>>>> I doubt this initial prefetch of the first 4 packets has any benefit.
>>>>
>>>> Ack will remove in v2 for x86.
>>>>
>>>>>
>>>>>> +	if (n_left_from >= 4) {
>>>>>> +		for (i = 0; i < 4; i++) {
>>>>>> +
>	rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>>>>>> +						       struct
>rte_ether_hdr
>>>>> *) +
>>>>>> +				      1);
>>>>>> +		}
>>>>>> +	}
>>>>>> +
>>>>>> +	/* Get stream for the speculated next node */
>>>>>> +	to_next = rte_node_next_stream_get(graph, node,
>>>>> next_index, nb_objs);
>>>>>
>>>>> Suggest you don't reuse the hand-unrolling optimization from
>FD.io
>>>>> VPP.
>>>>> I have never found any performance benefit from them, and they
>>>>> make the code unnecessarily verbose.
>>>>>
>>>>
>>>> How would be take the benefit of rte_lpm_lookupx4 without
>>> unrolling the loop?.
>>>> Also, in future if we are using rte_rib and fib with a CPU supporting
>>> wider SIMD we might
>>>> need to unroll them further (AVX256 AND 512 currently
>>> rte_lpm_lookup uses only 128bit
>>>> since it is only uses SSE extension).
>>>
>>> Let the compiler do it for you, but using a constant vector length.
>>> for (int i=0; i < 4; ++i) { ... }
>>>
>>
>> Ok, I think I misunderstood the previous comment.
>> It was only for the prefetches in the loop right?
>
>
>no, it was for all the needless repetition.
>hand-unrolling loops serve no purpose but to add verbosity.
>
>>
>>>>
>>>>>
>>>>>> +	while (n_left_from >= 4) {
>>>>>> +		/* Prefetch next-next mbufs */
>>>>>> +		if (likely(n_left_from >= 11)) {
>>>>>> +			rte_prefetch0(pkts[8]);
>>>>>> +			rte_prefetch0(pkts[9]);
>>>>>> +			rte_prefetch0(pkts[10]);
>>>>>> +			rte_prefetch0(pkts[11]);
>>>>>> +		}
>>>>>> +
>>>>>> +		/* Prefetch next mbuf data */
>>>>>> +		if (likely(n_left_from >= 7)) {
>>>>>> +
>	rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>>>>>> +						       struct
>rte_ether_hdr
>>>>> *) +
>>>>>> +				      1);
>>>>>> +
>	rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>>>>>> +						       struct
>rte_ether_hdr
>>>>> *) +
>>>>>> +				      1);
>>>>>> +
>	rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>>>>>> +						       struct
>rte_ether_hdr
>>>>> *) +
>>>>>> +				      1);
>>>>>> +
>	rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>>>>>> +						       struct
>rte_ether_hdr
>>>>> *) +
>>>>>> +				      1);
>>>>>> +		}
>>>>>> +
>>>>>> +		mbuf0 = pkts[0];
>>>>>> +		mbuf1 = pkts[1];
>>>>>> +		mbuf2 = pkts[2];
>>>>>> +		mbuf3 = pkts[3];
>>>>>> +
>>>>>> +		pkts += 4;
>>>>>> +		n_left_from -= 4;
>>>>>> +
>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>> rte_ether_hdr *);
>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>> +		ip0 = ipv4_hdr->dst_addr;
>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>ipv4_hdr-
>>>>>> hdr_checksum;
>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>> time_to_live;
>>>>>> +
>>>>>> +		/* Extract DIP of mbuf1 */
>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>>>>> rte_ether_hdr *);
>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>> +		ip1 = ipv4_hdr->dst_addr;
>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>> +		rte_node_mbuf_priv1(mbuf1)->cksum =
>ipv4_hdr-
>>>>>> hdr_checksum;
>>>>>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>>>>> time_to_live;
>>>>>> +
>>>>>> +		/* Extract DIP of mbuf2 */
>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>>>>> rte_ether_hdr *);
>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>> +		ip2 = ipv4_hdr->dst_addr;
>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>> +		rte_node_mbuf_priv1(mbuf2)->cksum =
>ipv4_hdr-
>>>>>> hdr_checksum;
>>>>>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>>>>> time_to_live;
>>>>>> +
>>>>>> +		/* Extract DIP of mbuf3 */
>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>>>>> rte_ether_hdr *);
>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>> +		ip3 = ipv4_hdr->dst_addr;
>>>>>> +
>>>>>> +		/* Prepare for lookup x4 */
>>>>>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>>>>>> +
>>>>>> +		/* Byte swap 4 IPV4 addresses. */
>>>>>> +		const __m128i bswap_mask = _mm_set_epi8(
>>>>>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1,
>2, 3);
>>>>>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>>>>>> +
>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>> +		rte_node_mbuf_priv1(mbuf3)->cksum =
>ipv4_hdr-
>>>>>> hdr_checksum;
>>>>>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>>>>> time_to_live;
>>>>>> +
>>>>>> +		/* Perform LPM lookup to get NH and next
>node */
>>>>>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>>>>>> +
>>>>>> +		/* Extract next node id and NH */
>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0]
>&
>>>>> 0xFFFF;
>>>>>> +		next0 = (dst.u32[0] >> 16);
>>>>>> +
>>>>>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1]
>&
>>>>> 0xFFFF;
>>>>>> +		next1 = (dst.u32[1] >> 16);
>>>>>> +
>>>>>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2]
>&
>>>>> 0xFFFF;
>>>>>> +		next2 = (dst.u32[2] >> 16);
>>>>>> +
>>>>>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3]
>&
>>>>> 0xFFFF;
>>>>>> +		next3 = (dst.u32[3] >> 16);
>>>>>> +
>>>>>> +		/* Enqueue four to next node */
>>>>>> +		rte_edge_t fix_spec =
>>>>>> +			(next_index ^ next0) | (next_index ^
>next1) |
>>>>>> +			(next_index ^ next2) | (next_index ^
>next3);
>>>>>> +
>>>>>> +		if (unlikely(fix_spec)) {
>>>>>> +			/* Copy things successfully speculated
>till now
>>>>> */
>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>> sizeof(from[0]));
>>>>>> +			from += last_spec;
>>>>>> +			to_next += last_spec;
>>>>>> +			held += last_spec;
>>>>>> +			last_spec = 0;
>>>>>> +
>>>>>> +			/* Next0 */
>>>>>> +			if (next_index == next0) {
>>>>>> +				to_next[0] = from[0];
>>>>>> +				to_next++;
>>>>>> +				held++;
>>>>>> +			} else {
>>>>>> +				rte_node_enqueue_x1(graph,
>node,
>>>>> next0,
>>>>>> +						    from[0]);
>>>>>> +			}
>>>>>> +
>>>>>> +			/* Next1 */
>>>>>> +			if (next_index == next1) {
>>>>>> +				to_next[0] = from[1];
>>>>>> +				to_next++;
>>>>>> +				held++;
>>>>>> +			} else {
>>>>>> +				rte_node_enqueue_x1(graph,
>node,
>>>>> next1,
>>>>>> +						    from[1]);
>>>>>> +			}
>>>>>> +
>>>>>> +			/* Next2 */
>>>>>> +			if (next_index == next2) {
>>>>>> +				to_next[0] = from[2];
>>>>>> +				to_next++;
>>>>>> +				held++;
>>>>>> +			} else {
>>>>>> +				rte_node_enqueue_x1(graph,
>node,
>>>>> next2,
>>>>>> +						    from[2]);
>>>>>> +			}
>>>>>> +
>>>>>> +			/* Next3 */
>>>>>> +			if (next_index == next3) {
>>>>>> +				to_next[0] = from[3];
>>>>>> +				to_next++;
>>>>>> +				held++;
>>>>>> +			} else {
>>>>>> +				rte_node_enqueue_x1(graph,
>node,
>>>>> next3,
>>>>>> +						    from[3]);
>>>>>> +			}
>>>>>> +
>>>>>> +			from += 4;
>>>>>> +
>>>>>> +		} else {
>>>>>> +			last_spec += 4;
>>>>>> +		}
>>>>>> +	}
>>>>>> +
>>>>>> +	while (n_left_from > 0) {
>>>>>> +		uint32_t next_hop;
>>>>>> +
>>>>>> +		mbuf0 = pkts[0];
>>>>>> +
>>>>>> +		pkts += 1;
>>>>>> +		n_left_from -= 1;
>>>>>> +
>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>> rte_ether_hdr *);
>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>ipv4_hdr-
>>>>>> hdr_checksum;
>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>> time_to_live;
>>>>>> +
>>>>>> +		rc = rte_lpm_lookup(lpm,
>rte_be_to_cpu_32(ipv4_hdr-
>>>>>> dst_addr),
>>>>>> +				    &next_hop);
>>>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>>>> +
>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop
>&
>>>>> 0xFFFF;
>>>>>> +		next0 = (next_hop >> 16);
>>>>>> +
>>>>>> +		if (unlikely(next_index ^ next0)) {
>>>>>> +			/* Copy things successfully speculated
>till now
>>>>> */
>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>> sizeof(from[0]));
>>>>>> +			from += last_spec;
>>>>>> +			to_next += last_spec;
>>>>>> +			held += last_spec;
>>>>>> +			last_spec = 0;
>>>>>> +
>>>>>> +			rte_node_enqueue_x1(graph, node,
>next0,
>>>>> from[0]);
>>>>>> +			from += 1;
>>>>>> +		} else {
>>>>>> +			last_spec += 1;
>>>>>> +		}
>>>>>> +	}
>>>>>> +
>>>>>> +	/* !!! Home run !!! */
>>>>>> +	if (likely(last_spec == nb_objs)) {
>>>>>> +		rte_node_next_stream_move(graph, node,
>>>>> next_index);
>>>>>> +		return nb_objs;
>>>>>> +	}
>>>>>> +
>>>>>> +	held += last_spec;
>>>>>> +	/* Copy things successfully speculated till now */
>>>>>> +	rte_memcpy(to_next, from, last_spec *
>sizeof(from[0]));
>>>>>> +	rte_node_next_stream_put(graph, node, next_index,
>held);
>>>>>> +
>>>>>> +	return nb_objs;
>>>>>> +}
>>>>>> +
>>>>>>  #else
>>>>>>
>>>>>>  static uint16_t
>>>>>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-24  9:40             ` Pavan Nikhilesh Bhagavatula
@ 2020-03-24 14:38               ` Ray Kinsella
  2020-03-26  9:56                 ` Pavan Nikhilesh Bhagavatula
  0 siblings, 1 reply; 219+ messages in thread
From: Ray Kinsella @ 2020-03-24 14:38 UTC (permalink / raw)
  To: Pavan Nikhilesh Bhagavatula, Jerin Jacob Kollanukkaran,
	Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda



On 24/03/2020 09:40, Pavan Nikhilesh Bhagavatula wrote:
> Hi Ray, 
> 
> I have tried to avoid hand unrolling loops and found the following observations.
> 
> 1. Although it decreases LOC it also takes away readability too.
> 	Example: 
> 	Avoiding unrolled code below
[SNIP] 
> 	Which is kind of unreadable.

I am confused - isn't it exactly the same code?
You still haven't completely unrolled the loop either?

I don't know how one is readable and the other is not. 

> 
> 2. Not all compilers are made equal. I found that most of the compilers don’t 
>      Unroll the loop above even when compiled with `-funroll-all-loops`.
>      I have checked with following compilers:
> 	GCC 9.2.0
> 	Clang 9.0.1
> 	Aarch64 GCC 7.3.0
> 	Aarch64 GCC 9.2.0

Compilers have been unrolling fixed length loops for as long time - this isn't new technology.

If the compiler isn't unrolling you are doing something that makes it think it is a bad idea.
Hand unrolling the loop isn't the solution, understanding what the compiler is doing is a better idea.

In front of your for loop insert, to indicate to the compiler what you want to do. 
#pragma unroll BUF_PER_LOOP 

With clang you can ask it why it is not unrolling the loop with the following switches. 
(output is verbose, but the reason is in there). 

-Rpass=loop-unroll -Rpass-missed=loop-unroll
 			
> 
> 3. Performance wise I see a lot of degradation on our platform at least 13%.

Is the loop being unrolled?

>     On IA with a Broadwell(Xeon E5-2690) and i40e the performance remain same w.r.t Rx/Tx since the 
>     hotspot is in the Tx path of the driver which limits the per core capability.
>     But the performance difference in number of cycles per node can be seen below:
> 
> 	Hand unrolling:
> +-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
> |Node                           |calls          |objs           |realloc_count  |objs/call      |objs/sec(10E6) |cycles/call|
> +-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
> |ip4_lookup                     |7765918        |248509344      |1              |32.000         |27.725408      |779.0000   |
> |ip4_rewrite                    |7765925        |248509568      |1              |32.000         |27.725408      |425.0000   |
> |ethdev_tx-1                    |7765927        |204056223      |1              |26.000         |22.762720      |597.0000   |
> |pkt_drop                       |1389170        |44453409       |1              |32.000         |4.962688       |298.0000   |
> |ethdev_rx-0-0                  |63604111       |248509792      |2              |32.000         |27.725408      |982.0000   |
> +-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
> 	
> 	W/o unrolling:
> 
> +-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
> |Node                           |calls          |objs           |realloc_count  |objs/call      |objs/sec(10E6) |cycles/call|
> +-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
> |ip4_lookup                     |18864640       |603668448      |1              |32.000         |26.051328      |828.0000   |
> |ip4_rewrite                    |18864646       |603668640      |1              |32.000         |26.051328      |534.0000   |
> |ethdev_tx-1                    |18864648       |527874175      |1              |27.000         |22.780256      |633.0000   |
> |pkt_drop                       |2368580        |75794529       |1              |32.000         |3.271072       |286.0000   |
> |ethdev_rx-0-0                  |282058226      |603668864      |2              |32.000         |26.051328      |994.0000   |
> +-------------------------------+---------------+---------------+---------------+---------------+---------------+-----------+
> 
> Considering the above findings we would like to continue unrolling the loops by hand.
> 
> Regards,
> Pavan.
> 
>> -----Original Message-----
>> From: Ray Kinsella <mdr@ashroe.eu>
>> Sent: Friday, March 20, 2020 2:44 PM
>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>; Jerin
>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar Dabilpuram
>> <ndabilpuram@marvell.com>
>> Cc: dev@dpdk.org; thomas@monjalon.net;
>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com; Kiran
>> Kumar Kokkilagadda <kirankumark@marvell.com>
>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup
>> for x86
>>
>>
>>
>> On 19/03/2020 16:13, Pavan Nikhilesh Bhagavatula wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Ray Kinsella <mdr@ashroe.eu>
>>>> Sent: Thursday, March 19, 2020 9:21 PM
>>>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>;
>> Jerin
>>>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar
>> Dabilpuram
>>>> <ndabilpuram@marvell.com>
>>>> Cc: dev@dpdk.org; thomas@monjalon.net;
>>>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com;
>> Kiran
>>>> Kumar Kokkilagadda <kirankumark@marvell.com>
>>>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4
>> lookup
>>>> for x86
>>>>
>>>>
>>>>
>>>> On 19/03/2020 14:22, Pavan Nikhilesh Bhagavatula wrote:
>>>>>> On 18/03/2020 21:35, jerinj@marvell.com wrote:
>>>>>>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>>>
>>>>>>> Add IPv4 lookup process function for ip4_lookup
>>>>>>> rte_node. This node performs LPM lookup using x86_64
>>>>>>> vector supported RTE_LPM API on every packet received
>>>>>>> and forwards it to a next node that is identified by
>>>>>>> lookup result.
>>>>>>>
>>>>>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
>>>>>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>>>>>> ---
>>>>>>>  lib/librte_node/ip4_lookup.c | 245
>>>>>> +++++++++++++++++++++++++++++++++++
>>>>>>>  1 file changed, 245 insertions(+)
>>>>>>>
>>>>>>> diff --git a/lib/librte_node/ip4_lookup.c
>>>>>> b/lib/librte_node/ip4_lookup.c
>>>>>>> index d7fcd1158..c003e9c91 100644
>>>>>>> --- a/lib/librte_node/ip4_lookup.c
>>>>>>> +++ b/lib/librte_node/ip4_lookup.c
>>>>>>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct
>>>> rte_graph
>>>>>> *graph, struct rte_node *node,
>>>>>>>  	return nb_objs;
>>>>>>>  }
>>>>>>>
>>>>>>> +#elif defined(RTE_ARCH_X86)
>>>>>>> +
>>>>>>> +/* X86 SSE */
>>>>>>> +static uint16_t
>>>>>>> +ip4_lookup_node_process(struct rte_graph *graph, struct
>>>> rte_node
>>>>>> *node,
>>>>>>> +			void **objs, uint16_t nb_objs)
>>>>>>> +{
>>>>>>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3,
>> **pkts;
>>>>>>> +	rte_edge_t next0, next1, next2, next3, next_index;
>>>>>>> +	struct rte_ipv4_hdr *ipv4_hdr;
>>>>>>> +	struct rte_ether_hdr *eth_hdr;
>>>>>>> +	uint32_t ip0, ip1, ip2, ip3;
>>>>>>> +	void **to_next, **from;
>>>>>>> +	uint16_t last_spec = 0;
>>>>>>> +	uint16_t n_left_from;
>>>>>>> +	struct rte_lpm *lpm;
>>>>>>> +	uint16_t held = 0;
>>>>>>> +	uint32_t drop_nh;
>>>>>>> +	rte_xmm_t dst;
>>>>>>> +	__m128i dip; /* SSE register */
>>>>>>> +	int rc, i;
>>>>>>> +
>>>>>>> +	/* Speculative next */
>>>>>>> +	next_index =
>> RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>>>>>>> +	/* Drop node */
>>>>>>> +	drop_nh =
>>>>>> ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>>>>>>> +
>>>>>>> +	/* Get socket specific LPM from ctx */
>>>>>>> +	lpm = *((struct rte_lpm **)node->ctx);
>>>>>>> +
>>>>>>> +	pkts = (struct rte_mbuf **)objs;
>>>>>>> +	from = objs;
>>>>>>> +	n_left_from = nb_objs;
>>>>>>
>>>>>> I doubt this initial prefetch of the first 4 packets has any benefit.
>>>>>
>>>>> Ack will remove in v2 for x86.
>>>>>
>>>>>>
>>>>>>> +	if (n_left_from >= 4) {
>>>>>>> +		for (i = 0; i < 4; i++) {
>>>>>>> +
>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>>>>>>> +						       struct
>> rte_ether_hdr
>>>>>> *) +
>>>>>>> +				      1);
>>>>>>> +		}
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	/* Get stream for the speculated next node */
>>>>>>> +	to_next = rte_node_next_stream_get(graph, node,
>>>>>> next_index, nb_objs);
>>>>>>
>>>>>> Suggest you don't reuse the hand-unrolling optimization from
>> FD.io
>>>>>> VPP.
>>>>>> I have never found any performance benefit from them, and they
>>>>>> make the code unnecessarily verbose.
>>>>>>
>>>>>
>>>>> How would be take the benefit of rte_lpm_lookupx4 without
>>>> unrolling the loop?.
>>>>> Also, in future if we are using rte_rib and fib with a CPU supporting
>>>> wider SIMD we might
>>>>> need to unroll them further (AVX256 AND 512 currently
>>>> rte_lpm_lookup uses only 128bit
>>>>> since it is only uses SSE extension).
>>>>
>>>> Let the compiler do it for you, but using a constant vector length.
>>>> for (int i=0; i < 4; ++i) { ... }
>>>>
>>>
>>> Ok, I think I misunderstood the previous comment.
>>> It was only for the prefetches in the loop right?
>>
>>
>> no, it was for all the needless repetition.
>> hand-unrolling loops serve no purpose but to add verbosity.
>>
>>>
>>>>>
>>>>>>
>>>>>>> +	while (n_left_from >= 4) {
>>>>>>> +		/* Prefetch next-next mbufs */
>>>>>>> +		if (likely(n_left_from >= 11)) {
>>>>>>> +			rte_prefetch0(pkts[8]);
>>>>>>> +			rte_prefetch0(pkts[9]);
>>>>>>> +			rte_prefetch0(pkts[10]);
>>>>>>> +			rte_prefetch0(pkts[11]);
>>>>>>> +		}
>>>>>>> +
>>>>>>> +		/* Prefetch next mbuf data */
>>>>>>> +		if (likely(n_left_from >= 7)) {
>>>>>>> +
>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>>>>>>> +						       struct
>> rte_ether_hdr
>>>>>> *) +
>>>>>>> +				      1);
>>>>>>> +
>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>>>>>>> +						       struct
>> rte_ether_hdr
>>>>>> *) +
>>>>>>> +				      1);
>>>>>>> +
>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>>>>>>> +						       struct
>> rte_ether_hdr
>>>>>> *) +
>>>>>>> +				      1);
>>>>>>> +
>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>>>>>>> +						       struct
>> rte_ether_hdr
>>>>>> *) +
>>>>>>> +				      1);
>>>>>>> +		}
>>>>>>> +
>>>>>>> +		mbuf0 = pkts[0];
>>>>>>> +		mbuf1 = pkts[1];
>>>>>>> +		mbuf2 = pkts[2];
>>>>>>> +		mbuf3 = pkts[3];
>>>>>>> +
>>>>>>> +		pkts += 4;
>>>>>>> +		n_left_from -= 4;
>>>>>>> +
>>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>>> rte_ether_hdr *);
>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>> +		ip0 = ipv4_hdr->dst_addr;
>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>> ipv4_hdr-
>>>>>>> hdr_checksum;
>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>>> time_to_live;
>>>>>>> +
>>>>>>> +		/* Extract DIP of mbuf1 */
>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>>>>>> rte_ether_hdr *);
>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>> +		ip1 = ipv4_hdr->dst_addr;
>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->cksum =
>> ipv4_hdr-
>>>>>>> hdr_checksum;
>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>>>>>> time_to_live;
>>>>>>> +
>>>>>>> +		/* Extract DIP of mbuf2 */
>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>>>>>> rte_ether_hdr *);
>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>> +		ip2 = ipv4_hdr->dst_addr;
>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->cksum =
>> ipv4_hdr-
>>>>>>> hdr_checksum;
>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>>>>>> time_to_live;
>>>>>>> +
>>>>>>> +		/* Extract DIP of mbuf3 */
>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>>>>>> rte_ether_hdr *);
>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>> +		ip3 = ipv4_hdr->dst_addr;
>>>>>>> +
>>>>>>> +		/* Prepare for lookup x4 */
>>>>>>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>>>>>>> +
>>>>>>> +		/* Byte swap 4 IPV4 addresses. */
>>>>>>> +		const __m128i bswap_mask = _mm_set_epi8(
>>>>>>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1,
>> 2, 3);
>>>>>>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>>>>>>> +
>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->cksum =
>> ipv4_hdr-
>>>>>>> hdr_checksum;
>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>>>>>> time_to_live;
>>>>>>> +
>>>>>>> +		/* Perform LPM lookup to get NH and next
>> node */
>>>>>>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>>>>>>> +
>>>>>>> +		/* Extract next node id and NH */
>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0]
>> &
>>>>>> 0xFFFF;
>>>>>>> +		next0 = (dst.u32[0] >> 16);
>>>>>>> +
>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1]
>> &
>>>>>> 0xFFFF;
>>>>>>> +		next1 = (dst.u32[1] >> 16);
>>>>>>> +
>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2]
>> &
>>>>>> 0xFFFF;
>>>>>>> +		next2 = (dst.u32[2] >> 16);
>>>>>>> +
>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3]
>> &
>>>>>> 0xFFFF;
>>>>>>> +		next3 = (dst.u32[3] >> 16);
>>>>>>> +
>>>>>>> +		/* Enqueue four to next node */
>>>>>>> +		rte_edge_t fix_spec =
>>>>>>> +			(next_index ^ next0) | (next_index ^
>> next1) |
>>>>>>> +			(next_index ^ next2) | (next_index ^
>> next3);
>>>>>>> +
>>>>>>> +		if (unlikely(fix_spec)) {
>>>>>>> +			/* Copy things successfully speculated
>> till now
>>>>>> */
>>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>>> sizeof(from[0]));
>>>>>>> +			from += last_spec;
>>>>>>> +			to_next += last_spec;
>>>>>>> +			held += last_spec;
>>>>>>> +			last_spec = 0;
>>>>>>> +
>>>>>>> +			/* Next0 */
>>>>>>> +			if (next_index == next0) {
>>>>>>> +				to_next[0] = from[0];
>>>>>>> +				to_next++;
>>>>>>> +				held++;
>>>>>>> +			} else {
>>>>>>> +				rte_node_enqueue_x1(graph,
>> node,
>>>>>> next0,
>>>>>>> +						    from[0]);
>>>>>>> +			}
>>>>>>> +
>>>>>>> +			/* Next1 */
>>>>>>> +			if (next_index == next1) {
>>>>>>> +				to_next[0] = from[1];
>>>>>>> +				to_next++;
>>>>>>> +				held++;
>>>>>>> +			} else {
>>>>>>> +				rte_node_enqueue_x1(graph,
>> node,
>>>>>> next1,
>>>>>>> +						    from[1]);
>>>>>>> +			}
>>>>>>> +
>>>>>>> +			/* Next2 */
>>>>>>> +			if (next_index == next2) {
>>>>>>> +				to_next[0] = from[2];
>>>>>>> +				to_next++;
>>>>>>> +				held++;
>>>>>>> +			} else {
>>>>>>> +				rte_node_enqueue_x1(graph,
>> node,
>>>>>> next2,
>>>>>>> +						    from[2]);
>>>>>>> +			}
>>>>>>> +
>>>>>>> +			/* Next3 */
>>>>>>> +			if (next_index == next3) {
>>>>>>> +				to_next[0] = from[3];
>>>>>>> +				to_next++;
>>>>>>> +				held++;
>>>>>>> +			} else {
>>>>>>> +				rte_node_enqueue_x1(graph,
>> node,
>>>>>> next3,
>>>>>>> +						    from[3]);
>>>>>>> +			}
>>>>>>> +
>>>>>>> +			from += 4;
>>>>>>> +
>>>>>>> +		} else {
>>>>>>> +			last_spec += 4;
>>>>>>> +		}
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	while (n_left_from > 0) {
>>>>>>> +		uint32_t next_hop;
>>>>>>> +
>>>>>>> +		mbuf0 = pkts[0];
>>>>>>> +
>>>>>>> +		pkts += 1;
>>>>>>> +		n_left_from -= 1;
>>>>>>> +
>>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>>> rte_ether_hdr *);
>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>> ipv4_hdr-
>>>>>>> hdr_checksum;
>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>>> time_to_live;
>>>>>>> +
>>>>>>> +		rc = rte_lpm_lookup(lpm,
>> rte_be_to_cpu_32(ipv4_hdr-
>>>>>>> dst_addr),
>>>>>>> +				    &next_hop);
>>>>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>>>>> +
>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop
>> &
>>>>>> 0xFFFF;
>>>>>>> +		next0 = (next_hop >> 16);
>>>>>>> +
>>>>>>> +		if (unlikely(next_index ^ next0)) {
>>>>>>> +			/* Copy things successfully speculated
>> till now
>>>>>> */
>>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>>> sizeof(from[0]));
>>>>>>> +			from += last_spec;
>>>>>>> +			to_next += last_spec;
>>>>>>> +			held += last_spec;
>>>>>>> +			last_spec = 0;
>>>>>>> +
>>>>>>> +			rte_node_enqueue_x1(graph, node,
>> next0,
>>>>>> from[0]);
>>>>>>> +			from += 1;
>>>>>>> +		} else {
>>>>>>> +			last_spec += 1;
>>>>>>> +		}
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	/* !!! Home run !!! */
>>>>>>> +	if (likely(last_spec == nb_objs)) {
>>>>>>> +		rte_node_next_stream_move(graph, node,
>>>>>> next_index);
>>>>>>> +		return nb_objs;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	held += last_spec;
>>>>>>> +	/* Copy things successfully speculated till now */
>>>>>>> +	rte_memcpy(to_next, from, last_spec *
>> sizeof(from[0]));
>>>>>>> +	rte_node_next_stream_put(graph, node, next_index,
>> held);
>>>>>>> +
>>>>>>> +	return nb_objs;
>>>>>>> +}
>>>>>>> +
>>>>>>>  #else
>>>>>>>
>>>>>>>  static uint16_t
>>>>>>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-24 14:38               ` Ray Kinsella
@ 2020-03-26  9:56                 ` Pavan Nikhilesh Bhagavatula
  2020-03-26 16:54                   ` Ray Kinsella
  0 siblings, 1 reply; 219+ messages in thread
From: Pavan Nikhilesh Bhagavatula @ 2020-03-26  9:56 UTC (permalink / raw)
  To: Ray Kinsella, Jerin Jacob Kollanukkaran, Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda


>-----Original Message-----
>From: Ray Kinsella <mdr@ashroe.eu>
>Sent: Tuesday, March 24, 2020 8:08 PM
>To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>; Jerin
>Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar Dabilpuram
><ndabilpuram@marvell.com>
>Cc: dev@dpdk.org; thomas@monjalon.net;
>david.marchand@redhat.com; mattias.ronnblom@ericsson.com; Kiran
>Kumar Kokkilagadda <kirankumark@marvell.com>
>Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup
>for x86
>
>
>
>On 24/03/2020 09:40, Pavan Nikhilesh Bhagavatula wrote:
>> Hi Ray,
>>
>> I have tried to avoid hand unrolling loops and found the following
>observations.
>>
>> 1. Although it decreases LOC it also takes away readability too.
>> 	Example:
>> 	Avoiding unrolled code below
>[SNIP]
>> 	Which is kind of unreadable.
>
>I am confused - isn't it exactly the same code?
>You still haven't completely unrolled the loop either?
>
>I don't know how one is readable and the other is not.

I guess it’s a matter of personal preference.

>
>>
>> 2. Not all compilers are made equal. I found that most of the
>compilers don’t
>>      Unroll the loop above even when compiled with `-funroll-all-loops`.
>>      I have checked with following compilers:
>> 	GCC 9.2.0
>> 	Clang 9.0.1
>> 	Aarch64 GCC 7.3.0
>> 	Aarch64 GCC 9.2.0
>
>Compilers have been unrolling fixed length loops for as long time - this
>isn't new technology.
>

In theory, I agree with your view, but even the latest compiler is not doing a decent 
job on unrolling the loop. 
We can revisit this scheme, if and when compiler smart enough to do this as just unrolling 
the loops is not good enough. It has to do it better than hand unrolling.
For example on arm64 GCC doesn’t merge load/stores to load/store pairs when unrolling.
(both ldr/str and ldp/stp latency is 3cyc and effectively ldp cost is halved).

Even on x86 we see extra ~100 cycles as mentioned in [1].

>If the compiler isn't unrolling you are doing something that makes it
>think it is a bad idea.
>Hand unrolling the loop isn't the solution, understanding what the
>compiler is doing is a better idea.
>
>In front of your for loop insert, to indicate to the compiler what you
>want to do.
>#pragma unroll BUF_PER_LOOP

Can you check which versions of compiler which this pragma works on?

Most of the gcc versions that I have tried just spit out the following warning.
../lib/librte_node/ip4_rewrite.c:59: warning: ignoring #pragma unroll BUF_PER_LOOP [-Wunknown-pragmas]
   59 |  #pragma unroll BUF_PER_LOOP


>
>With clang you can ask it why it is not unrolling the loop with the
>following switches.
>(output is verbose, but the reason is in there).
>
>-Rpass=loop-unroll -Rpass-missed=loop-unroll
>

../lib/librte_node/ip4_rewrite.c:57:2: remark: unrolled loop by a factor of 4 with run-time trip count [-Rpass=loop-unroll]
        for (i = 0; i < nb_objs; i++) {
        ^

>>
>> 3. Performance wise I see a lot of degradation on our platform at least
>13%.
>
>Is the loop being unrolled?
>

Yes, in a suboptimal way. https://pastebin.com/nkXvzMiW

we decide to stick with hand unrolling based on the test result instead of depending on compilers mercy to do it for us[2].
if you think, it can be improved then please submit a patch.

[2]https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88760

>>     On IA with a Broadwell(Xeon E5-2690) and i40e the performance
>remain same w.r.t Rx/Tx since the
>>     hotspot is in the Tx path of the driver which limits the per core
>capability.
>>     But the performance difference in number of cycles per node can
>be seen below:
>>

[1]

>> 	Hand unrolling:
>> +-------------------------------+---------------+---------------+---------------+-
>--------------+---------------+-----------+
>> |Node                           |calls          |objs           |realloc_count  |objs/call
>|objs/sec(10E6) |cycles/call|
>> +-------------------------------+---------------+---------------+---------------+-
>--------------+---------------+-----------+
>> |ip4_lookup                     |7765918        |248509344      |1              |32.000
>|27.725408      |779.0000   |
>> |ip4_rewrite                    |7765925        |248509568      |1              |32.000
>|27.725408      |425.0000   |
>> |ethdev_tx-1                    |7765927        |204056223      |1              |26.000
>|22.762720      |597.0000   |
>> |pkt_drop                       |1389170        |44453409       |1              |32.000
>|4.962688       |298.0000   |
>> |ethdev_rx-0-0                  |63604111       |248509792      |2              |32.000
>|27.725408      |982.0000   |
>> +-------------------------------+---------------+---------------+---------------+-
>--------------+---------------+-----------+
>>
>> 	W/o unrolling:
>>
>> +-------------------------------+---------------+---------------+---------------+-
>--------------+---------------+-----------+
>> |Node                           |calls          |objs           |realloc_count  |objs/call
>|objs/sec(10E6) |cycles/call|
>> +-------------------------------+---------------+---------------+---------------+-
>--------------+---------------+-----------+
>> |ip4_lookup                     |18864640       |603668448      |1              |32.000
>|26.051328      |828.0000   |
>> |ip4_rewrite                    |18864646       |603668640      |1              |32.000
>|26.051328      |534.0000   |
>> |ethdev_tx-1                    |18864648       |527874175      |1              |27.000
>|22.780256      |633.0000   |
>> |pkt_drop                       |2368580        |75794529       |1              |32.000
>|3.271072       |286.0000   |
>> |ethdev_rx-0-0                  |282058226      |603668864      |2              |32.000
>|26.051328      |994.0000   |
>> +-------------------------------+---------------+---------------+---------------+-
>--------------+---------------+-----------+
>>
>> Considering the above findings we would like to continue unrolling
>the loops by hand.
>>
>> Regards,
>> Pavan.
>>
>>> -----Original Message-----
>>> From: Ray Kinsella <mdr@ashroe.eu>
>>> Sent: Friday, March 20, 2020 2:44 PM
>>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>;
>Jerin
>>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar
>Dabilpuram
>>> <ndabilpuram@marvell.com>
>>> Cc: dev@dpdk.org; thomas@monjalon.net;
>>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com;
>Kiran
>>> Kumar Kokkilagadda <kirankumark@marvell.com>
>>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4
>lookup
>>> for x86
>>>
>>>
>>>
>>> On 19/03/2020 16:13, Pavan Nikhilesh Bhagavatula wrote:
>>>>
>>>>
>>>>> -----Original Message-----
>>>>> From: Ray Kinsella <mdr@ashroe.eu>
>>>>> Sent: Thursday, March 19, 2020 9:21 PM
>>>>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>;
>>> Jerin
>>>>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar
>>> Dabilpuram
>>>>> <ndabilpuram@marvell.com>
>>>>> Cc: dev@dpdk.org; thomas@monjalon.net;
>>>>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com;
>>> Kiran
>>>>> Kumar Kokkilagadda <kirankumark@marvell.com>
>>>>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4
>>> lookup
>>>>> for x86
>>>>>
>>>>>
>>>>>
>>>>> On 19/03/2020 14:22, Pavan Nikhilesh Bhagavatula wrote:
>>>>>>> On 18/03/2020 21:35, jerinj@marvell.com wrote:
>>>>>>>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>>>>
>>>>>>>> Add IPv4 lookup process function for ip4_lookup
>>>>>>>> rte_node. This node performs LPM lookup using x86_64
>>>>>>>> vector supported RTE_LPM API on every packet received
>>>>>>>> and forwards it to a next node that is identified by
>>>>>>>> lookup result.
>>>>>>>>
>>>>>>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>>>> Signed-off-by: Nithin Dabilpuram
><ndabilpuram@marvell.com>
>>>>>>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>>>>>>> ---
>>>>>>>>  lib/librte_node/ip4_lookup.c | 245
>>>>>>> +++++++++++++++++++++++++++++++++++
>>>>>>>>  1 file changed, 245 insertions(+)
>>>>>>>>
>>>>>>>> diff --git a/lib/librte_node/ip4_lookup.c
>>>>>>> b/lib/librte_node/ip4_lookup.c
>>>>>>>> index d7fcd1158..c003e9c91 100644
>>>>>>>> --- a/lib/librte_node/ip4_lookup.c
>>>>>>>> +++ b/lib/librte_node/ip4_lookup.c
>>>>>>>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct
>>>>> rte_graph
>>>>>>> *graph, struct rte_node *node,
>>>>>>>>  	return nb_objs;
>>>>>>>>  }
>>>>>>>>
>>>>>>>> +#elif defined(RTE_ARCH_X86)
>>>>>>>> +
>>>>>>>> +/* X86 SSE */
>>>>>>>> +static uint16_t
>>>>>>>> +ip4_lookup_node_process(struct rte_graph *graph, struct
>>>>> rte_node
>>>>>>> *node,
>>>>>>>> +			void **objs, uint16_t nb_objs)
>>>>>>>> +{
>>>>>>>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3,
>>> **pkts;
>>>>>>>> +	rte_edge_t next0, next1, next2, next3, next_index;
>>>>>>>> +	struct rte_ipv4_hdr *ipv4_hdr;
>>>>>>>> +	struct rte_ether_hdr *eth_hdr;
>>>>>>>> +	uint32_t ip0, ip1, ip2, ip3;
>>>>>>>> +	void **to_next, **from;
>>>>>>>> +	uint16_t last_spec = 0;
>>>>>>>> +	uint16_t n_left_from;
>>>>>>>> +	struct rte_lpm *lpm;
>>>>>>>> +	uint16_t held = 0;
>>>>>>>> +	uint32_t drop_nh;
>>>>>>>> +	rte_xmm_t dst;
>>>>>>>> +	__m128i dip; /* SSE register */
>>>>>>>> +	int rc, i;
>>>>>>>> +
>>>>>>>> +	/* Speculative next */
>>>>>>>> +	next_index =
>>> RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>>>>>>>> +	/* Drop node */
>>>>>>>> +	drop_nh =
>>>>>>> ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>>>>>>>> +
>>>>>>>> +	/* Get socket specific LPM from ctx */
>>>>>>>> +	lpm = *((struct rte_lpm **)node->ctx);
>>>>>>>> +
>>>>>>>> +	pkts = (struct rte_mbuf **)objs;
>>>>>>>> +	from = objs;
>>>>>>>> +	n_left_from = nb_objs;
>>>>>>>
>>>>>>> I doubt this initial prefetch of the first 4 packets has any
>benefit.
>>>>>>
>>>>>> Ack will remove in v2 for x86.
>>>>>>
>>>>>>>
>>>>>>>> +	if (n_left_from >= 4) {
>>>>>>>> +		for (i = 0; i < 4; i++) {
>>>>>>>> +
>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>>>>>>>> +						       struct
>>> rte_ether_hdr
>>>>>>> *) +
>>>>>>>> +				      1);
>>>>>>>> +		}
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	/* Get stream for the speculated next node */
>>>>>>>> +	to_next = rte_node_next_stream_get(graph, node,
>>>>>>> next_index, nb_objs);
>>>>>>>
>>>>>>> Suggest you don't reuse the hand-unrolling optimization from
>>> FD.io
>>>>>>> VPP.
>>>>>>> I have never found any performance benefit from them, and
>they
>>>>>>> make the code unnecessarily verbose.
>>>>>>>
>>>>>>
>>>>>> How would be take the benefit of rte_lpm_lookupx4 without
>>>>> unrolling the loop?.
>>>>>> Also, in future if we are using rte_rib and fib with a CPU
>supporting
>>>>> wider SIMD we might
>>>>>> need to unroll them further (AVX256 AND 512 currently
>>>>> rte_lpm_lookup uses only 128bit
>>>>>> since it is only uses SSE extension).
>>>>>
>>>>> Let the compiler do it for you, but using a constant vector length.
>>>>> for (int i=0; i < 4; ++i) { ... }
>>>>>
>>>>
>>>> Ok, I think I misunderstood the previous comment.
>>>> It was only for the prefetches in the loop right?
>>>
>>>
>>> no, it was for all the needless repetition.
>>> hand-unrolling loops serve no purpose but to add verbosity.
>>>
>>>>
>>>>>>
>>>>>>>
>>>>>>>> +	while (n_left_from >= 4) {
>>>>>>>> +		/* Prefetch next-next mbufs */
>>>>>>>> +		if (likely(n_left_from >= 11)) {
>>>>>>>> +			rte_prefetch0(pkts[8]);
>>>>>>>> +			rte_prefetch0(pkts[9]);
>>>>>>>> +			rte_prefetch0(pkts[10]);
>>>>>>>> +			rte_prefetch0(pkts[11]);
>>>>>>>> +		}
>>>>>>>> +
>>>>>>>> +		/* Prefetch next mbuf data */
>>>>>>>> +		if (likely(n_left_from >= 7)) {
>>>>>>>> +
>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>>>>>>>> +						       struct
>>> rte_ether_hdr
>>>>>>> *) +
>>>>>>>> +				      1);
>>>>>>>> +
>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>>>>>>>> +						       struct
>>> rte_ether_hdr
>>>>>>> *) +
>>>>>>>> +				      1);
>>>>>>>> +
>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>>>>>>>> +						       struct
>>> rte_ether_hdr
>>>>>>> *) +
>>>>>>>> +				      1);
>>>>>>>> +
>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>>>>>>>> +						       struct
>>> rte_ether_hdr
>>>>>>> *) +
>>>>>>>> +				      1);
>>>>>>>> +		}
>>>>>>>> +
>>>>>>>> +		mbuf0 = pkts[0];
>>>>>>>> +		mbuf1 = pkts[1];
>>>>>>>> +		mbuf2 = pkts[2];
>>>>>>>> +		mbuf3 = pkts[3];
>>>>>>>> +
>>>>>>>> +		pkts += 4;
>>>>>>>> +		n_left_from -= 4;
>>>>>>>> +
>>>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>>>> rte_ether_hdr *);
>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>> +		ip0 = ipv4_hdr->dst_addr;
>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>>> ipv4_hdr-
>>>>>>>> hdr_checksum;
>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>>>> time_to_live;
>>>>>>>> +
>>>>>>>> +		/* Extract DIP of mbuf1 */
>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>>>>>>> rte_ether_hdr *);
>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>> +		ip1 = ipv4_hdr->dst_addr;
>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->cksum =
>>> ipv4_hdr-
>>>>>>>> hdr_checksum;
>>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>>>>>>> time_to_live;
>>>>>>>> +
>>>>>>>> +		/* Extract DIP of mbuf2 */
>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>>>>>>> rte_ether_hdr *);
>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>> +		ip2 = ipv4_hdr->dst_addr;
>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->cksum =
>>> ipv4_hdr-
>>>>>>>> hdr_checksum;
>>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>>>>>>> time_to_live;
>>>>>>>> +
>>>>>>>> +		/* Extract DIP of mbuf3 */
>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>>>>>>> rte_ether_hdr *);
>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>> +		ip3 = ipv4_hdr->dst_addr;
>>>>>>>> +
>>>>>>>> +		/* Prepare for lookup x4 */
>>>>>>>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>>>>>>>> +
>>>>>>>> +		/* Byte swap 4 IPV4 addresses. */
>>>>>>>> +		const __m128i bswap_mask = _mm_set_epi8(
>>>>>>>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1,
>>> 2, 3);
>>>>>>>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>>>>>>>> +
>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->cksum =
>>> ipv4_hdr-
>>>>>>>> hdr_checksum;
>>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>>>>>>> time_to_live;
>>>>>>>> +
>>>>>>>> +		/* Perform LPM lookup to get NH and next
>>> node */
>>>>>>>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>>>>>>>> +
>>>>>>>> +		/* Extract next node id and NH */
>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0]
>>> &
>>>>>>> 0xFFFF;
>>>>>>>> +		next0 = (dst.u32[0] >> 16);
>>>>>>>> +
>>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1]
>>> &
>>>>>>> 0xFFFF;
>>>>>>>> +		next1 = (dst.u32[1] >> 16);
>>>>>>>> +
>>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2]
>>> &
>>>>>>> 0xFFFF;
>>>>>>>> +		next2 = (dst.u32[2] >> 16);
>>>>>>>> +
>>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3]
>>> &
>>>>>>> 0xFFFF;
>>>>>>>> +		next3 = (dst.u32[3] >> 16);
>>>>>>>> +
>>>>>>>> +		/* Enqueue four to next node */
>>>>>>>> +		rte_edge_t fix_spec =
>>>>>>>> +			(next_index ^ next0) | (next_index ^
>>> next1) |
>>>>>>>> +			(next_index ^ next2) | (next_index ^
>>> next3);
>>>>>>>> +
>>>>>>>> +		if (unlikely(fix_spec)) {
>>>>>>>> +			/* Copy things successfully speculated
>>> till now
>>>>>>> */
>>>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>>>> sizeof(from[0]));
>>>>>>>> +			from += last_spec;
>>>>>>>> +			to_next += last_spec;
>>>>>>>> +			held += last_spec;
>>>>>>>> +			last_spec = 0;
>>>>>>>> +
>>>>>>>> +			/* Next0 */
>>>>>>>> +			if (next_index == next0) {
>>>>>>>> +				to_next[0] = from[0];
>>>>>>>> +				to_next++;
>>>>>>>> +				held++;
>>>>>>>> +			} else {
>>>>>>>> +				rte_node_enqueue_x1(graph,
>>> node,
>>>>>>> next0,
>>>>>>>> +						    from[0]);
>>>>>>>> +			}
>>>>>>>> +
>>>>>>>> +			/* Next1 */
>>>>>>>> +			if (next_index == next1) {
>>>>>>>> +				to_next[0] = from[1];
>>>>>>>> +				to_next++;
>>>>>>>> +				held++;
>>>>>>>> +			} else {
>>>>>>>> +				rte_node_enqueue_x1(graph,
>>> node,
>>>>>>> next1,
>>>>>>>> +						    from[1]);
>>>>>>>> +			}
>>>>>>>> +
>>>>>>>> +			/* Next2 */
>>>>>>>> +			if (next_index == next2) {
>>>>>>>> +				to_next[0] = from[2];
>>>>>>>> +				to_next++;
>>>>>>>> +				held++;
>>>>>>>> +			} else {
>>>>>>>> +				rte_node_enqueue_x1(graph,
>>> node,
>>>>>>> next2,
>>>>>>>> +						    from[2]);
>>>>>>>> +			}
>>>>>>>> +
>>>>>>>> +			/* Next3 */
>>>>>>>> +			if (next_index == next3) {
>>>>>>>> +				to_next[0] = from[3];
>>>>>>>> +				to_next++;
>>>>>>>> +				held++;
>>>>>>>> +			} else {
>>>>>>>> +				rte_node_enqueue_x1(graph,
>>> node,
>>>>>>> next3,
>>>>>>>> +						    from[3]);
>>>>>>>> +			}
>>>>>>>> +
>>>>>>>> +			from += 4;
>>>>>>>> +
>>>>>>>> +		} else {
>>>>>>>> +			last_spec += 4;
>>>>>>>> +		}
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	while (n_left_from > 0) {
>>>>>>>> +		uint32_t next_hop;
>>>>>>>> +
>>>>>>>> +		mbuf0 = pkts[0];
>>>>>>>> +
>>>>>>>> +		pkts += 1;
>>>>>>>> +		n_left_from -= 1;
>>>>>>>> +
>>>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>>>> rte_ether_hdr *);
>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>>> ipv4_hdr-
>>>>>>>> hdr_checksum;
>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>>>> time_to_live;
>>>>>>>> +
>>>>>>>> +		rc = rte_lpm_lookup(lpm,
>>> rte_be_to_cpu_32(ipv4_hdr-
>>>>>>>> dst_addr),
>>>>>>>> +				    &next_hop);
>>>>>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>>>>>> +
>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop
>>> &
>>>>>>> 0xFFFF;
>>>>>>>> +		next0 = (next_hop >> 16);
>>>>>>>> +
>>>>>>>> +		if (unlikely(next_index ^ next0)) {
>>>>>>>> +			/* Copy things successfully speculated
>>> till now
>>>>>>> */
>>>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>>>> sizeof(from[0]));
>>>>>>>> +			from += last_spec;
>>>>>>>> +			to_next += last_spec;
>>>>>>>> +			held += last_spec;
>>>>>>>> +			last_spec = 0;
>>>>>>>> +
>>>>>>>> +			rte_node_enqueue_x1(graph, node,
>>> next0,
>>>>>>> from[0]);
>>>>>>>> +			from += 1;
>>>>>>>> +		} else {
>>>>>>>> +			last_spec += 1;
>>>>>>>> +		}
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	/* !!! Home run !!! */
>>>>>>>> +	if (likely(last_spec == nb_objs)) {
>>>>>>>> +		rte_node_next_stream_move(graph, node,
>>>>>>> next_index);
>>>>>>>> +		return nb_objs;
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	held += last_spec;
>>>>>>>> +	/* Copy things successfully speculated till now */
>>>>>>>> +	rte_memcpy(to_next, from, last_spec *
>>> sizeof(from[0]));
>>>>>>>> +	rte_node_next_stream_put(graph, node, next_index,
>>> held);
>>>>>>>> +
>>>>>>>> +	return nb_objs;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>  #else
>>>>>>>>
>>>>>>>>  static uint16_t
>>>>>>>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v1 20/26] node: ipv4 lookup for x86
  2020-03-26  9:56                 ` Pavan Nikhilesh Bhagavatula
@ 2020-03-26 16:54                   ` Ray Kinsella
  0 siblings, 0 replies; 219+ messages in thread
From: Ray Kinsella @ 2020-03-26 16:54 UTC (permalink / raw)
  To: Pavan Nikhilesh Bhagavatula, Jerin Jacob Kollanukkaran,
	Nithin Kumar Dabilpuram
  Cc: dev, thomas, david.marchand, mattias.ronnblom, Kiran Kumar Kokkilagadda



On 26/03/2020 09:56, Pavan Nikhilesh Bhagavatula wrote:
> 
>> -----Original Message-----
>> From: Ray Kinsella <mdr@ashroe.eu>
>> Sent: Tuesday, March 24, 2020 8:08 PM
>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>; Jerin
>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar Dabilpuram
>> <ndabilpuram@marvell.com>
>> Cc: dev@dpdk.org; thomas@monjalon.net;
>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com; Kiran
>> Kumar Kokkilagadda <kirankumark@marvell.com>
>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup
>> for x86
>>
>>
>>
>> On 24/03/2020 09:40, Pavan Nikhilesh Bhagavatula wrote:
>>> Hi Ray,
>>>
>>> I have tried to avoid hand unrolling loops and found the following
>> observations.
>>>
>>> 1. Although it decreases LOC it also takes away readability too.
>>> 	Example:
>>> 	Avoiding unrolled code below
>> [SNIP]
>>> 	Which is kind of unreadable.
>>
>> I am confused - isn't it exactly the same code?
>> You still haven't completely unrolled the loop either?
>>
>> I don't know how one is readable and the other is not.
> 
> I guess it’s a matter of personal preference.

You have a rare preference for verbosity, sir.

> 
>>
>>>
>>> 2. Not all compilers are made equal. I found that most of the
>> compilers don’t
>>>      Unroll the loop above even when compiled with `-funroll-all-loops`.
>>>      I have checked with following compilers:
>>> 	GCC 9.2.0
>>> 	Clang 9.0.1
>>> 	Aarch64 GCC 7.3.0
>>> 	Aarch64 GCC 9.2.0
>>
>> Compilers have been unrolling fixed length loops for as long time - this
>> isn't new technology.
>>
> 
> In theory, I agree with your view, but even the latest compiler is not doing a decent 
> job on unrolling the loop. 
> We can revisit this scheme, if and when compiler smart enough to do this as just unrolling 
> the loops is not good enough. It has to do it better than hand unrolling.
> For example on arm64 GCC doesn’t merge load/stores to load/store pairs when unrolling.
> (both ldr/str and ldp/stp latency is 3cyc and effectively ldp cost is halved).
> 
> Even on x86 we see extra ~100 cycles as mentioned in [1].
> 
>> If the compiler isn't unrolling you are doing something that makes it
>> think it is a bad idea.
>> Hand unrolling the loop isn't the solution, understanding what the
>> compiler is doing is a better idea.
>>
>> In front of your for loop insert, to indicate to the compiler what you
>> want to do.
>> #pragma unroll BUF_PER_LOOP
> 
> Can you check which versions of compiler which this pragma works on?

https://gcc.gnu.org/onlinedocs/gcc/Loop-Specific-Pragmas.html
https://clang.llvm.org/docs/AttributeReference.htm

> 
> Most of the gcc versions that I have tried just spit out the following warning.
> ../lib/librte_node/ip4_rewrite.c:59: warning: ignoring #pragma unroll BUF_PER_LOOP [-Wunknown-pragmas]
>    59 |  #pragma unroll BUF_PER_LOOP

Not sure on this one ... 

>>
>> With clang you can ask it why it is not unrolling the loop with the
>> following switches.
>> (output is verbose, but the reason is in there).
>>
>> -Rpass=loop-unroll -Rpass-missed=loop-unroll
>>
> 
> ../lib/librte_node/ip4_rewrite.c:57:2: remark: unrolled loop by a factor of 4 with run-time trip count [-Rpass=loop-unroll]
>         for (i = 0; i < nb_objs; i++) {
>         ^

That is good ... 

>>>
>>> 3. Performance wise I see a lot of degradation on our platform at least
>> 13%.
>>
>> Is the loop being unrolled?
>>
> 
> Yes, in a suboptimal way. https://pastebin.com/nkXvzMiW
> 
> we decide to stick with hand unrolling based on the test result instead of depending on compilers mercy to do it for us[2].

Why not code in assembler then, taking to it logical conclusion.

> if you think, it can be improved then please submit a patch.

I don't have an ARM platform to test on.

From my own work in FD.io VPP, which you are clear borrowing heavily from, to put it kindly. 
I have never seen an advantage from hand-unrolling compared to letting the compiler do it.

Perhaps this case is the exception.
I would be suspicious though that the compiler is making bad decisions for some reason.

In anycase, if you (and the presumabily the maintainer) are happy with verbose code like this making it's way into the codebase. 
I won't argue with it.

> [2]https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88760
> 
>>>     On IA with a Broadwell(Xeon E5-2690) and i40e the performance
>> remain same w.r.t Rx/Tx since the
>>>     hotspot is in the Tx path of the driver which limits the per core
>> capability.
>>>     But the performance difference in number of cycles per node can
>> be seen below:
>>>
> 
> [1]
> 
>>> 	Hand unrolling:
>>> +-------------------------------+---------------+---------------+---------------+-
>> --------------+---------------+-----------+
>>> |Node                           |calls          |objs           |realloc_count  |objs/call
>> |objs/sec(10E6) |cycles/call|
>>> +-------------------------------+---------------+---------------+---------------+-
>> --------------+---------------+-----------+
>>> |ip4_lookup                     |7765918        |248509344      |1              |32.000
>> |27.725408      |779.0000   |
>>> |ip4_rewrite                    |7765925        |248509568      |1              |32.000
>> |27.725408      |425.0000   |
>>> |ethdev_tx-1                    |7765927        |204056223      |1              |26.000
>> |22.762720      |597.0000   |
>>> |pkt_drop                       |1389170        |44453409       |1              |32.000
>> |4.962688       |298.0000   |
>>> |ethdev_rx-0-0                  |63604111       |248509792      |2              |32.000
>> |27.725408      |982.0000   |
>>> +-------------------------------+---------------+---------------+---------------+-
>> --------------+---------------+-----------+
>>>
>>> 	W/o unrolling:
>>>
>>> +-------------------------------+---------------+---------------+---------------+-
>> --------------+---------------+-----------+
>>> |Node                           |calls          |objs           |realloc_count  |objs/call
>> |objs/sec(10E6) |cycles/call|
>>> +-------------------------------+---------------+---------------+---------------+-
>> --------------+---------------+-----------+
>>> |ip4_lookup                     |18864640       |603668448      |1              |32.000
>> |26.051328      |828.0000   |
>>> |ip4_rewrite                    |18864646       |603668640      |1              |32.000
>> |26.051328      |534.0000   |
>>> |ethdev_tx-1                    |18864648       |527874175      |1              |27.000
>> |22.780256      |633.0000   |
>>> |pkt_drop                       |2368580        |75794529       |1              |32.000
>> |3.271072       |286.0000   |
>>> |ethdev_rx-0-0                  |282058226      |603668864      |2              |32.000
>> |26.051328      |994.0000   |
>>> +-------------------------------+---------------+---------------+---------------+-
>> --------------+---------------+-----------+
>>>
>>> Considering the above findings we would like to continue unrolling
>> the loops by hand.
>>>
>>> Regards,
>>> Pavan.
>>>
>>>> -----Original Message-----
>>>> From: Ray Kinsella <mdr@ashroe.eu>
>>>> Sent: Friday, March 20, 2020 2:44 PM
>>>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>;
>> Jerin
>>>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar
>> Dabilpuram
>>>> <ndabilpuram@marvell.com>
>>>> Cc: dev@dpdk.org; thomas@monjalon.net;
>>>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com;
>> Kiran
>>>> Kumar Kokkilagadda <kirankumark@marvell.com>
>>>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4
>> lookup
>>>> for x86
>>>>
>>>>
>>>>
>>>> On 19/03/2020 16:13, Pavan Nikhilesh Bhagavatula wrote:
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Ray Kinsella <mdr@ashroe.eu>
>>>>>> Sent: Thursday, March 19, 2020 9:21 PM
>>>>>> To: Pavan Nikhilesh Bhagavatula <pbhagavatula@marvell.com>;
>>>> Jerin
>>>>>> Jacob Kollanukkaran <jerinj@marvell.com>; Nithin Kumar
>>>> Dabilpuram
>>>>>> <ndabilpuram@marvell.com>
>>>>>> Cc: dev@dpdk.org; thomas@monjalon.net;
>>>>>> david.marchand@redhat.com; mattias.ronnblom@ericsson.com;
>>>> Kiran
>>>>>> Kumar Kokkilagadda <kirankumark@marvell.com>
>>>>>> Subject: Re: [EXT] Re: [dpdk-dev] [PATCH v1 20/26] node: ipv4
>>>> lookup
>>>>>> for x86
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 19/03/2020 14:22, Pavan Nikhilesh Bhagavatula wrote:
>>>>>>>> On 18/03/2020 21:35, jerinj@marvell.com wrote:
>>>>>>>>> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>>>>>
>>>>>>>>> Add IPv4 lookup process function for ip4_lookup
>>>>>>>>> rte_node. This node performs LPM lookup using x86_64
>>>>>>>>> vector supported RTE_LPM API on every packet received
>>>>>>>>> and forwards it to a next node that is identified by
>>>>>>>>> lookup result.
>>>>>>>>>
>>>>>>>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>>>>>>>> Signed-off-by: Nithin Dabilpuram
>> <ndabilpuram@marvell.com>
>>>>>>>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>>>>>>>> ---
>>>>>>>>>  lib/librte_node/ip4_lookup.c | 245
>>>>>>>> +++++++++++++++++++++++++++++++++++
>>>>>>>>>  1 file changed, 245 insertions(+)
>>>>>>>>>
>>>>>>>>> diff --git a/lib/librte_node/ip4_lookup.c
>>>>>>>> b/lib/librte_node/ip4_lookup.c
>>>>>>>>> index d7fcd1158..c003e9c91 100644
>>>>>>>>> --- a/lib/librte_node/ip4_lookup.c
>>>>>>>>> +++ b/lib/librte_node/ip4_lookup.c
>>>>>>>>> @@ -264,6 +264,251 @@ ip4_lookup_node_process(struct
>>>>>> rte_graph
>>>>>>>> *graph, struct rte_node *node,
>>>>>>>>>  	return nb_objs;
>>>>>>>>>  }
>>>>>>>>>
>>>>>>>>> +#elif defined(RTE_ARCH_X86)
>>>>>>>>> +
>>>>>>>>> +/* X86 SSE */
>>>>>>>>> +static uint16_t
>>>>>>>>> +ip4_lookup_node_process(struct rte_graph *graph, struct
>>>>>> rte_node
>>>>>>>> *node,
>>>>>>>>> +			void **objs, uint16_t nb_objs)
>>>>>>>>> +{
>>>>>>>>> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3,
>>>> **pkts;
>>>>>>>>> +	rte_edge_t next0, next1, next2, next3, next_index;
>>>>>>>>> +	struct rte_ipv4_hdr *ipv4_hdr;
>>>>>>>>> +	struct rte_ether_hdr *eth_hdr;
>>>>>>>>> +	uint32_t ip0, ip1, ip2, ip3;
>>>>>>>>> +	void **to_next, **from;
>>>>>>>>> +	uint16_t last_spec = 0;
>>>>>>>>> +	uint16_t n_left_from;
>>>>>>>>> +	struct rte_lpm *lpm;
>>>>>>>>> +	uint16_t held = 0;
>>>>>>>>> +	uint32_t drop_nh;
>>>>>>>>> +	rte_xmm_t dst;
>>>>>>>>> +	__m128i dip; /* SSE register */
>>>>>>>>> +	int rc, i;
>>>>>>>>> +
>>>>>>>>> +	/* Speculative next */
>>>>>>>>> +	next_index =
>>>> RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
>>>>>>>>> +	/* Drop node */
>>>>>>>>> +	drop_nh =
>>>>>>>> ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
>>>>>>>>> +
>>>>>>>>> +	/* Get socket specific LPM from ctx */
>>>>>>>>> +	lpm = *((struct rte_lpm **)node->ctx);
>>>>>>>>> +
>>>>>>>>> +	pkts = (struct rte_mbuf **)objs;
>>>>>>>>> +	from = objs;
>>>>>>>>> +	n_left_from = nb_objs;
>>>>>>>>
>>>>>>>> I doubt this initial prefetch of the first 4 packets has any
>> benefit.
>>>>>>>
>>>>>>> Ack will remove in v2 for x86.
>>>>>>>
>>>>>>>>
>>>>>>>>> +	if (n_left_from >= 4) {
>>>>>>>>> +		for (i = 0; i < 4; i++) {
>>>>>>>>> +
>>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[i],
>>>>>>>>> +						       struct
>>>> rte_ether_hdr
>>>>>>>> *) +
>>>>>>>>> +				      1);
>>>>>>>>> +		}
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	/* Get stream for the speculated next node */
>>>>>>>>> +	to_next = rte_node_next_stream_get(graph, node,
>>>>>>>> next_index, nb_objs);
>>>>>>>>
>>>>>>>> Suggest you don't reuse the hand-unrolling optimization from
>>>> FD.io
>>>>>>>> VPP.
>>>>>>>> I have never found any performance benefit from them, and
>> they
>>>>>>>> make the code unnecessarily verbose.
>>>>>>>>
>>>>>>>
>>>>>>> How would be take the benefit of rte_lpm_lookupx4 without
>>>>>> unrolling the loop?.
>>>>>>> Also, in future if we are using rte_rib and fib with a CPU
>> supporting
>>>>>> wider SIMD we might
>>>>>>> need to unroll them further (AVX256 AND 512 currently
>>>>>> rte_lpm_lookup uses only 128bit
>>>>>>> since it is only uses SSE extension).
>>>>>>
>>>>>> Let the compiler do it for you, but using a constant vector length.
>>>>>> for (int i=0; i < 4; ++i) { ... }
>>>>>>
>>>>>
>>>>> Ok, I think I misunderstood the previous comment.
>>>>> It was only for the prefetches in the loop right?
>>>>
>>>>
>>>> no, it was for all the needless repetition.
>>>> hand-unrolling loops serve no purpose but to add verbosity.
>>>>
>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>> +	while (n_left_from >= 4) {
>>>>>>>>> +		/* Prefetch next-next mbufs */
>>>>>>>>> +		if (likely(n_left_from >= 11)) {
>>>>>>>>> +			rte_prefetch0(pkts[8]);
>>>>>>>>> +			rte_prefetch0(pkts[9]);
>>>>>>>>> +			rte_prefetch0(pkts[10]);
>>>>>>>>> +			rte_prefetch0(pkts[11]);
>>>>>>>>> +		}
>>>>>>>>> +
>>>>>>>>> +		/* Prefetch next mbuf data */
>>>>>>>>> +		if (likely(n_left_from >= 7)) {
>>>>>>>>> +
>>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[4],
>>>>>>>>> +						       struct
>>>> rte_ether_hdr
>>>>>>>> *) +
>>>>>>>>> +				      1);
>>>>>>>>> +
>>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[5],
>>>>>>>>> +						       struct
>>>> rte_ether_hdr
>>>>>>>> *) +
>>>>>>>>> +				      1);
>>>>>>>>> +
>>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[6],
>>>>>>>>> +						       struct
>>>> rte_ether_hdr
>>>>>>>> *) +
>>>>>>>>> +				      1);
>>>>>>>>> +
>>>> 	rte_prefetch0(rte_pktmbuf_mtod(pkts[7],
>>>>>>>>> +						       struct
>>>> rte_ether_hdr
>>>>>>>> *) +
>>>>>>>>> +				      1);
>>>>>>>>> +		}
>>>>>>>>> +
>>>>>>>>> +		mbuf0 = pkts[0];
>>>>>>>>> +		mbuf1 = pkts[1];
>>>>>>>>> +		mbuf2 = pkts[2];
>>>>>>>>> +		mbuf3 = pkts[3];
>>>>>>>>> +
>>>>>>>>> +		pkts += 4;
>>>>>>>>> +		n_left_from -= 4;
>>>>>>>>> +
>>>>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>>>>> rte_ether_hdr *);
>>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>>> +		ip0 = ipv4_hdr->dst_addr;
>>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>>>> ipv4_hdr-
>>>>>>>>> hdr_checksum;
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>>>>> time_to_live;
>>>>>>>>> +
>>>>>>>>> +		/* Extract DIP of mbuf1 */
>>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct
>>>>>>>> rte_ether_hdr *);
>>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>>> +		ip1 = ipv4_hdr->dst_addr;
>>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->cksum =
>>>> ipv4_hdr-
>>>>>>>>> hdr_checksum;
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr-
>>>>>>>>> time_to_live;
>>>>>>>>> +
>>>>>>>>> +		/* Extract DIP of mbuf2 */
>>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct
>>>>>>>> rte_ether_hdr *);
>>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>>> +		ip2 = ipv4_hdr->dst_addr;
>>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->cksum =
>>>> ipv4_hdr-
>>>>>>>>> hdr_checksum;
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr-
>>>>>>>>> time_to_live;
>>>>>>>>> +
>>>>>>>>> +		/* Extract DIP of mbuf3 */
>>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct
>>>>>>>> rte_ether_hdr *);
>>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>>> +		ip3 = ipv4_hdr->dst_addr;
>>>>>>>>> +
>>>>>>>>> +		/* Prepare for lookup x4 */
>>>>>>>>> +		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
>>>>>>>>> +
>>>>>>>>> +		/* Byte swap 4 IPV4 addresses. */
>>>>>>>>> +		const __m128i bswap_mask = _mm_set_epi8(
>>>>>>>>> +			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1,
>>>> 2, 3);
>>>>>>>>> +		dip = _mm_shuffle_epi8(dip, bswap_mask);
>>>>>>>>> +
>>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->cksum =
>>>> ipv4_hdr-
>>>>>>>>> hdr_checksum;
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr-
>>>>>>>>> time_to_live;
>>>>>>>>> +
>>>>>>>>> +		/* Perform LPM lookup to get NH and next
>>>> node */
>>>>>>>>> +		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
>>>>>>>>> +
>>>>>>>>> +		/* Extract next node id and NH */
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0]
>>>> &
>>>>>>>> 0xFFFF;
>>>>>>>>> +		next0 = (dst.u32[0] >> 16);
>>>>>>>>> +
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1]
>>>> &
>>>>>>>> 0xFFFF;
>>>>>>>>> +		next1 = (dst.u32[1] >> 16);
>>>>>>>>> +
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2]
>>>> &
>>>>>>>> 0xFFFF;
>>>>>>>>> +		next2 = (dst.u32[2] >> 16);
>>>>>>>>> +
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3]
>>>> &
>>>>>>>> 0xFFFF;
>>>>>>>>> +		next3 = (dst.u32[3] >> 16);
>>>>>>>>> +
>>>>>>>>> +		/* Enqueue four to next node */
>>>>>>>>> +		rte_edge_t fix_spec =
>>>>>>>>> +			(next_index ^ next0) | (next_index ^
>>>> next1) |
>>>>>>>>> +			(next_index ^ next2) | (next_index ^
>>>> next3);
>>>>>>>>> +
>>>>>>>>> +		if (unlikely(fix_spec)) {
>>>>>>>>> +			/* Copy things successfully speculated
>>>> till now
>>>>>>>> */
>>>>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>>>>> sizeof(from[0]));
>>>>>>>>> +			from += last_spec;
>>>>>>>>> +			to_next += last_spec;
>>>>>>>>> +			held += last_spec;
>>>>>>>>> +			last_spec = 0;
>>>>>>>>> +
>>>>>>>>> +			/* Next0 */
>>>>>>>>> +			if (next_index == next0) {
>>>>>>>>> +				to_next[0] = from[0];
>>>>>>>>> +				to_next++;
>>>>>>>>> +				held++;
>>>>>>>>> +			} else {
>>>>>>>>> +				rte_node_enqueue_x1(graph,
>>>> node,
>>>>>>>> next0,
>>>>>>>>> +						    from[0]);
>>>>>>>>> +			}
>>>>>>>>> +
>>>>>>>>> +			/* Next1 */
>>>>>>>>> +			if (next_index == next1) {
>>>>>>>>> +				to_next[0] = from[1];
>>>>>>>>> +				to_next++;
>>>>>>>>> +				held++;
>>>>>>>>> +			} else {
>>>>>>>>> +				rte_node_enqueue_x1(graph,
>>>> node,
>>>>>>>> next1,
>>>>>>>>> +						    from[1]);
>>>>>>>>> +			}
>>>>>>>>> +
>>>>>>>>> +			/* Next2 */
>>>>>>>>> +			if (next_index == next2) {
>>>>>>>>> +				to_next[0] = from[2];
>>>>>>>>> +				to_next++;
>>>>>>>>> +				held++;
>>>>>>>>> +			} else {
>>>>>>>>> +				rte_node_enqueue_x1(graph,
>>>> node,
>>>>>>>> next2,
>>>>>>>>> +						    from[2]);
>>>>>>>>> +			}
>>>>>>>>> +
>>>>>>>>> +			/* Next3 */
>>>>>>>>> +			if (next_index == next3) {
>>>>>>>>> +				to_next[0] = from[3];
>>>>>>>>> +				to_next++;
>>>>>>>>> +				held++;
>>>>>>>>> +			} else {
>>>>>>>>> +				rte_node_enqueue_x1(graph,
>>>> node,
>>>>>>>> next3,
>>>>>>>>> +						    from[3]);
>>>>>>>>> +			}
>>>>>>>>> +
>>>>>>>>> +			from += 4;
>>>>>>>>> +
>>>>>>>>> +		} else {
>>>>>>>>> +			last_spec += 4;
>>>>>>>>> +		}
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	while (n_left_from > 0) {
>>>>>>>>> +		uint32_t next_hop;
>>>>>>>>> +
>>>>>>>>> +		mbuf0 = pkts[0];
>>>>>>>>> +
>>>>>>>>> +		pkts += 1;
>>>>>>>>> +		n_left_from -= 1;
>>>>>>>>> +
>>>>>>>>> +		/* Extract DIP of mbuf0 */
>>>>>>>>> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct
>>>>>>>> rte_ether_hdr *);
>>>>>>>>> +		ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
>>>>>>>>> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->cksum =
>>>> ipv4_hdr-
>>>>>>>>> hdr_checksum;
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr-
>>>>>>>>> time_to_live;
>>>>>>>>> +
>>>>>>>>> +		rc = rte_lpm_lookup(lpm,
>>>> rte_be_to_cpu_32(ipv4_hdr-
>>>>>>>>> dst_addr),
>>>>>>>>> +				    &next_hop);
>>>>>>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>>>>>>> +
>>>>>>>>> +		rte_node_mbuf_priv1(mbuf0)->nh = next_hop
>>>> &
>>>>>>>> 0xFFFF;
>>>>>>>>> +		next0 = (next_hop >> 16);
>>>>>>>>> +
>>>>>>>>> +		if (unlikely(next_index ^ next0)) {
>>>>>>>>> +			/* Copy things successfully speculated
>>>> till now
>>>>>>>> */
>>>>>>>>> +			rte_memcpy(to_next, from, last_spec *
>>>>>>>> sizeof(from[0]));
>>>>>>>>> +			from += last_spec;
>>>>>>>>> +			to_next += last_spec;
>>>>>>>>> +			held += last_spec;
>>>>>>>>> +			last_spec = 0;
>>>>>>>>> +
>>>>>>>>> +			rte_node_enqueue_x1(graph, node,
>>>> next0,
>>>>>>>> from[0]);
>>>>>>>>> +			from += 1;
>>>>>>>>> +		} else {
>>>>>>>>> +			last_spec += 1;
>>>>>>>>> +		}
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	/* !!! Home run !!! */
>>>>>>>>> +	if (likely(last_spec == nb_objs)) {
>>>>>>>>> +		rte_node_next_stream_move(graph, node,
>>>>>>>> next_index);
>>>>>>>>> +		return nb_objs;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	held += last_spec;
>>>>>>>>> +	/* Copy things successfully speculated till now */
>>>>>>>>> +	rte_memcpy(to_next, from, last_spec *
>>>> sizeof(from[0]));
>>>>>>>>> +	rte_node_next_stream_put(graph, node, next_index,
>>>> held);
>>>>>>>>> +
>>>>>>>>> +	return nb_objs;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>>  #else
>>>>>>>>>
>>>>>>>>>  static uint16_t
>>>>>>>>>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 00/28] graph: introduce graph subsystem
  2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
                   ` (25 preceding siblings ...)
  2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 26/26] l3fwd-graph: add graph config and main loop jerinj
@ 2020-03-26 16:56 ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 01/28] graph: define the public API for graph support jerinj
                     ` (29 more replies)
  26 siblings, 30 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Using graph traversal for packet processing is a proven architecture
that has been implemented in various open source libraries.

Graph architecture for packet processing enables abstracting the data
processing functions as “nodes” and “links” them together to create a
complex “graph” to create reusable/modular data processing functions. 

The patchset further includes performance enhancements and modularity
to the DPDK as discussed in more detail below.

v2..v1:
------
1) Added programmer guide/implementation documentation and l3fwd-graph doc

Hosted in netlify for easy reference:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Programmer’s Guide:
https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html

l3fwd-graph doc:
https://dpdk-graph.netlify.com/doc/html/guides/sample_app_ug/l3_forward_graph.html

API doc:
https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__graph__worker_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__eth__api_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__ip4__api_8h.html

2) Added the release notes for the this feature

3) Fix build issues reported by CI for v1:
http://mails.dpdk.org/archives/test-report/2020-March/121326.html

RFC..v1:
--------

1) Split the patch to more logical ones for review.
2) Added doxygen comments for the API
3) Code cleanup
4) Additional performance improvements.
Delta between l3fwd and l3fwd-graph is negligible now.
(~1%) on octeontx2.
5) Added SIMD routines for x86 in additional to arm64.

Addional nodes planned for v20.08
----------------------------------
1) Packet classification node
2) Support for IPV6 LPM node


This patchset contains
-----------------------------
1) The API definition to "create" nodes and "link" together to create a
"graph" for packet processing. See, lib/librte_graph/rte_graph.h  

2) The Fast path API definition for the graph walker and enqueue
function used by the workers. See, lib/librte_graph/rte_graph_worker.h

3) Optimized SW implementation for (1) and (2). See, lib/librte_graph/

4) Test case to verify the graph infrastructure functionality
See, app/test/test_graph.c
 
5) Performance test cases to evaluate the cost of graph walker and nodes
enqueue fast-path function for various combinations.

See app/test/test_graph_perf.c

6) Packet processing nodes(Null, Rx, Tx, Pkt drop, IPV4 rewrite, IPv4
lookup)
using graph infrastructure. See lib/librte_node/*

7) An example application to showcase l3fwd
(functionality same as existing examples/l3fwd) using graph
infrastructure and use packets processing nodes (item (6)). See examples/l3fwd-graph/.

Performance
-----------
1) Graph walk and node enqueue overhead can be tested with performance
test case application [1]
# If all packets go from a node to another node (we call it as
# "homerun") then it will be just a pointer swap for a burst of packets.
# In the worst case, a couple of handful cycles to move an object from a
node to another node.

2) Performance comparison with existing l3fwd (The complete static code
with out any nodes) vs modular l3fwd-graph with 5 nodes
(ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
Here is graphical representation of the l3fwd-graph as Graphviz dot
file: 
http://bit.ly/39UPPGm

# l3fwd-graph performance is -1.2% wrt static l3fwd.

# We have simulated the similar test with existing librte_pipeline
# application [4].
ip_pipline application is -48.62% wrt static l3fwd.

The above results are on octeontx2. It may vary on other platforms.
The platforms with higher L1 and L2 caches will have further better
performance.


Tested architectures:
--------------------
1) AArch64
2) X86


Identified tweaking for better performance on different targets
---------------------------------------------------------------
1) Test with various burst size values (256, 128, 64, 32) using
CONFIG_RTE_GRAPH_BURST_SIZE config option.
Based on our testing, on x86 and arm64 servers, The sweet spot is 256
burst size.
While on arm64 embedded SoCs, it is either 64 or 128.

2) Disable node statistics (use CONFIG_RTE_LIBRTE_GRAPH_STATS config
option)
if not needed.

3) Use arm64 optimized memory copy for arm64 architecture by
selecting CONFIG_RTE_ARCH_ARM64_MEMCPY. 

Commands to run tests
---------------------

[1] 
perf test:
echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[2]
functionality test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[3]
l3fwd-graph:
./l3fwd-graph -c 0x100  -- -p 0x3 --config="(0, 0, 8)" -P

[4]
# ./ip_pipeline --c 0xff0000 -- -s route.cli

Route.cli: (Copy paste to the shell to avoid dos format issues)

https://pastebin.com/raw/B4Ktx7TT

Jerin Jacob (13):
  graph: define the public API for graph support
  graph: implement node registration
  graph: implement node operations
  graph: implement node debug routines
  graph: implement internal graph operation helpers
  graph: populate fastpath memory for graph reel
  graph: implement create and destroy APIs
  graph: implement graph operation APIs
  graph: implement Graphviz export
  graph: implement debug routines
  graph: implement stats support
  graph: implement fastpath API routines
  doc: add graph library programmer's guide guide

Kiran Kumar K (2):
  graph: add unit test case
  node: add ipv4 rewrite node

Nithin Dabilpuram (11):
  node: add log infra and null node
  node: add ethdev Rx node
  node: add ethdev Tx node
  node: add ethdev Rx and Tx node ctrl API
  node: ipv4 lookup for arm64
  node: add ipv4 rewrite and lookup ctrl API
  node: add pkt drop node
  l3fwd-graph: add graph based l3fwd skeleton
  l3fwd-graph: add ethdev configuration changes
  l3fwd-graph: add graph config and main loop
  doc: add l3fwd graph application user guide

Pavan Nikhilesh (2):
  graph: add performance testcase
  node: ipv4 lookup for x86

 MAINTAINERS                                   |   14 +
 app/test/Makefile                             |    7 +
 app/test/meson.build                          |   12 +-
 app/test/test_graph.c                         |  819 ++++
 app/test/test_graph_perf.c                    | 1057 ++++++
 config/common_base                            |   12 +
 config/rte_config.h                           |    4 +
 doc/api/doxy-api-index.md                     |    5 +
 doc/api/doxy-api.conf.in                      |    2 +
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   32 +
 doc/guides/sample_app_ug/index.rst            |    1 +
 doc/guides/sample_app_ug/intro.rst            |    4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst |  327 ++
 examples/Makefile                             |    3 +
 examples/l3fwd-graph/Makefile                 |   58 +
 examples/l3fwd-graph/main.c                   | 1111 ++++++
 examples/l3fwd-graph/meson.build              |   13 +
 examples/meson.build                          |    6 +-
 lib/Makefile                                  |    6 +
 lib/librte_graph/Makefile                     |   28 +
 lib/librte_graph/graph.c                      |  589 +++
 lib/librte_graph/graph_debug.c                |   84 +
 lib/librte_graph/graph_ops.c                  |  169 +
 lib/librte_graph/graph_populate.c             |  234 ++
 lib/librte_graph/graph_private.h              |  347 ++
 lib/librte_graph/graph_stats.c                |  406 ++
 lib/librte_graph/meson.build                  |   11 +
 lib/librte_graph/node.c                       |  421 +++
 lib/librte_graph/rte_graph.h                  |  786 ++++
 lib/librte_graph/rte_graph_version.map        |   47 +
 lib/librte_graph/rte_graph_worker.h           |  542 +++
 lib/librte_node/Makefile                      |   32 +
 lib/librte_node/ethdev_ctrl.c                 |  116 +
 lib/librte_node/ethdev_rx.c                   |  221 ++
 lib/librte_node/ethdev_rx_priv.h              |   81 +
 lib/librte_node/ethdev_tx.c                   |   86 +
 lib/librte_node/ethdev_tx_priv.h              |   62 +
 lib/librte_node/ip4_lookup.c                  |  619 +++
 lib/librte_node/ip4_rewrite.c                 |  326 ++
 lib/librte_node/ip4_rewrite_priv.h            |   77 +
 lib/librte_node/log.c                         |   14 +
 lib/librte_node/meson.build                   |   10 +
 lib/librte_node/node_private.h                |   96 +
 lib/librte_node/null.c                        |   23 +
 lib/librte_node/pkt_drop.c                    |   26 +
 lib/librte_node/rte_node_eth_api.h            |   70 +
 lib/librte_node/rte_node_ip4_api.h            |   87 +
 lib/librte_node/rte_node_version.map          |    9 +
 lib/meson.build                               |    5 +-
 meson.build                                   |    1 +
 mk/rte.app.mk                                 |    2 +
 56 files changed, 14623 insertions(+), 5 deletions(-)
 create mode 100644 app/test/test_graph.c
 create mode 100644 app/test/test_graph_perf.c
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/graph_debug.c
 create mode 100644 lib/librte_graph/graph_ops.c
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/graph_stats.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/node.c
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map
 create mode 100644 lib/librte_graph/rte_graph_worker.h
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/pkt_drop.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h
 create mode 100644 lib/librte_node/rte_node_ip4_api.h
 create mode 100644 lib/librte_node/rte_node_version.map

-- 
2.25.1


^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 01/28] graph: define the public API for graph support
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 02/28] graph: implement node registration jerinj
                     ` (28 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Thomas Monjalon, Bruce Richardson, John McNamara,
	Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, david.marchand, mdr, mattias.ronnblom, pbhagavatula, ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Graph architecture abstracts the data processing functions as
"node" and "link" them together to create a complex "graph" to enable
reusable/modular data processing functions.

These APIs enables graph framework operations such as create, lookup,
dump and destroy on graph and node operations such as clone,
edge update, and edge shrink, etc. The API also allows creating the
stats cluster to monitor per graph and per node stats.

This patch defines the public API for graph support.
This patch also adds support for the build infrastructure and
update the MAINTAINERS file for the graph subsystem.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                            |   5 +
 config/common_base                     |   7 +
 config/rte_config.h                    |   4 +
 doc/api/doxy-api-index.md              |   1 +
 doc/api/doxy-api.conf.in               |   1 +
 lib/Makefile                           |   3 +
 lib/librte_graph/Makefile              |  22 +
 lib/librte_graph/graph.c               |   5 +
 lib/librte_graph/meson.build           |  11 +
 lib/librte_graph/rte_graph.h           | 786 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   3 +
 lib/meson.build                        |   2 +-
 mk/rte.app.mk                          |   1 +
 13 files changed, 850 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index db235c2cc..bc7085983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1469,6 +1469,11 @@ F: examples/bpf/
 F: app/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Graph - EXPERIMENTAL
+M: Jerin Jacob <jerinj@marvell.com>
+M: Kiran Kumar K <kirankumark@marvell.com>
+F: lib/librte_graph/
+
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index c31175f9d..32f982136 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1074,6 +1074,13 @@ CONFIG_RTE_LIBRTE_BPF_ELF=n
 #
 CONFIG_RTE_LIBRTE_IPSEC=y
 
+#
+# Compile librte_graph
+#
+CONFIG_RTE_LIBRTE_GRAPH=y
+CONFIG_RTE_GRAPH_BURST_SIZE=256
+CONFIG_RTE_LIBRTE_GRAPH_STATS=y
+
 #
 # Compile the test application
 #
diff --git a/config/rte_config.h b/config/rte_config.h
index d30786bc0..e9201fd46 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -98,6 +98,10 @@
 /* KNI defines */
 #define RTE_KNI_PREEMPT_DEFAULT 1
 
+/* rte_graph defines */
+#define RTE_GRAPH_BURST_SIZE 256
+#define RTE_LIBRTE_GRAPH_STATS 1
+
 /****** driver defines ********/
 
 /* QuickAssist device */
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index dff496be0..5cc50f750 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -159,6 +159,7 @@ The public API headers are grouped by topics:
   * [pipeline]         (@ref rte_pipeline.h)
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
+  * [graph]            (@ref rte_graph.h):
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 1c4392eec..759a7213e 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -33,6 +33,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_eventdev \
                           @TOPDIR@/lib/librte_fib \
                           @TOPDIR@/lib/librte_flow_classify \
+                          @TOPDIR@/lib/librte_graph \
                           @TOPDIR@/lib/librte_gro \
                           @TOPDIR@/lib/librte_gso \
                           @TOPDIR@/lib/librte_hash \
diff --git a/lib/Makefile b/lib/Makefile
index 46b91ae1a..1f572b659 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -119,6 +119,9 @@ DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
 DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu
 DEPDIRS-librte_rcu := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
+DEPDIRS-librte_graph := librte_eal
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
new file mode 100644
index 000000000..26fe514f3
--- /dev/null
+++ b/lib/librte_graph/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_graph.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal
+
+EXPORT_MAP := rte_graph_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
new file mode 100644
index 000000000..a55bf443a
--- /dev/null
+++ b/lib/librte_graph/graph.c
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "rte_graph.h"
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
new file mode 100644
index 000000000..455cf2ba5
--- /dev/null
+++ b/lib/librte_graph/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+name = 'graph'
+
+sources = files('graph.c')
+headers = files('rte_graph.h')
+allow_experimental_apis = true
+
+deps += ['eal']
diff --git a/lib/librte_graph/rte_graph.h b/lib/librte_graph/rte_graph.h
new file mode 100644
index 000000000..4bcf0a6e5
--- /dev/null
+++ b/lib/librte_graph/rte_graph.h
@@ -0,0 +1,786 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_H_
+#define _RTE_GRAPH_H_
+
+/**
+ * @file rte_graph.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph architecture abstracts the data processing functions as
+ * "node" and "link" them together to create a complex "graph" to enable
+ * reusable/modular data processing functions.
+ *
+ * This API enables graph framework operations such as create, lookup,
+ * dump and destroy on graph and node operations such as clone,
+ * edge update, and edge shrink, etc. The API also allows to create the stats
+ * cluster to monitor per graph and per node stats.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_GRAPH_NAMESIZE 64 /**< Max length of graph name. */
+#define RTE_NODE_NAMESIZE 64  /**< Max length of node name. */
+#define RTE_GRAPH_OFF_INVALID UINT32_MAX /**< Invalid graph offset. */
+#define RTE_NODE_ID_INVALID UINT32_MAX   /**< Invalid node id. */
+#define RTE_EDGE_ID_INVALID UINT16_MAX   /**< Invalid edge id. */
+#define RTE_GRAPH_ID_INVALID UINT16_MAX  /**< Invalid graph id. */
+#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL /**< Graph fence data. */
+
+typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
+typedef uint32_t rte_node_t;       /**< Node id type. */
+typedef uint16_t rte_edge_t;       /**< Edge id type. */
+typedef uint16_t rte_graph_t;      /**< Graph id type. */
+
+/** Burst size in terms of log2 */
+#if RTE_GRAPH_BURST_SIZE == 1
+#define RTE_GRAPH_BURST_SIZE_LOG2 0  /**< Object burst size of 1. */
+#elif RTE_GRAPH_BURST_SIZE == 2
+#define RTE_GRAPH_BURST_SIZE_LOG2 1  /**< Object burst size of 2. */
+#elif RTE_GRAPH_BURST_SIZE == 4
+#define RTE_GRAPH_BURST_SIZE_LOG2 2  /**< Object burst size of 4. */
+#elif RTE_GRAPH_BURST_SIZE == 8
+#define RTE_GRAPH_BURST_SIZE_LOG2 3  /**< Object burst size of 8. */
+#elif RTE_GRAPH_BURST_SIZE == 16
+#define RTE_GRAPH_BURST_SIZE_LOG2 4  /**< Object burst size of 16. */
+#elif RTE_GRAPH_BURST_SIZE == 32
+#define RTE_GRAPH_BURST_SIZE_LOG2 5  /**< Object burst size of 32. */
+#elif RTE_GRAPH_BURST_SIZE == 64
+#define RTE_GRAPH_BURST_SIZE_LOG2 6  /**< Object burst size of 64. */
+#elif RTE_GRAPH_BURST_SIZE == 128
+#define RTE_GRAPH_BURST_SIZE_LOG2 7  /**< Object burst size of 128. */
+#elif RTE_GRAPH_BURST_SIZE == 256
+#define RTE_GRAPH_BURST_SIZE_LOG2 8  /**< Object burst size of 256. */
+#else
+#error "Unsupported burst size"
+#endif
+
+/* Forward declaration */
+struct rte_node;  /**< Node data */
+struct rte_graph; /**< Graph data */
+struct rte_graph_cluster_stats;      /**< Stats for Cluster of graphs */
+struct rte_graph_cluster_node_stats; /**< Node stats within cluster of graphs */
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node process function.
+ *
+ * The function invoked when the worker thread walks on nodes using
+ * rte_graph_walk().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param objs
+ *   Pointer to an array of objects to be processed.
+ * @param nb_objs
+ *   Number of objects in the array.
+ *
+ * @return
+ *   Number of objects processed.
+ *
+ * @see rte_graph_walk()
+ *
+ */
+typedef uint16_t (*rte_node_process_t)(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node initialization function.
+ *
+ * The function invoked when the user creates the graph using rte_graph_create()
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ *
+ * @see rte_graph_create()
+ */
+typedef int (*rte_node_init_t)(const struct rte_graph *graph,
+			       struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node finalization function.
+ *
+ * The function invoked when the user destroys the graph using
+ * rte_graph_destroy().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @see rte_graph_destroy()
+ */
+typedef void (*rte_node_fini_t)(const struct rte_graph *graph,
+				struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph cluster stats callback.
+ *
+ * @param is_first
+ *   Flag to denote that stats are of the first node.
+ * @param is_last
+ *   Flag to denote that stats are of the last node.
+ * @param cookie
+ *   Cookie supplied during stats creation.
+ * @param stats
+ *   Node cluster stats data.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ */
+typedef int (*rte_graph_cluster_stats_cb_t)(bool is_first, bool is_last,
+	     void *cookie, const struct rte_graph_cluster_node_stats *stats);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for creating the graph.
+ *
+ * @see rte_graph_create()
+ */
+struct rte_graph_param {
+	int socket_id; /**< Socket id where memory is allocated. */
+	uint16_t nb_node_patterns;  /**< Number of node patterns. */
+	const char **node_patterns;
+	/**< Array of node patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for graph cluster stats create.
+ *
+ * @see rte_graph_cluster_stats_create()
+ */
+struct rte_graph_cluster_stats_param {
+	int socket_id;
+	/**< Socket id where memory is allocated */
+	rte_graph_cluster_stats_cb_t fn;
+	/**< Stats print callback function. NULL value allowed, in that case,
+	 *   default print stat function used.
+	 */
+	RTE_STD_C11
+	union {
+		void *cookie;
+		FILE *f; /**< File pointer to dump the stats when fn == NULL. */
+	};
+	uint16_t nb_graph_patterns;  /**< Number of graph patterns. */
+	const char **graph_patterns;
+	/**< Array of graph patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node cluster stats data structure.
+ *
+ * @see struct rte_graph_cluster_stats_param::fn
+ */
+struct rte_graph_cluster_node_stats {
+	uint64_t ts;	    /**< Current timestamp. */
+	uint64_t calls;	    /**< Current number of calls made. */
+	uint64_t objs;      /**< Current number of objs processed. */
+	uint64_t cycles;    /**< Current number of cycles. */
+
+	uint64_t prev_ts;	/**< Previous call timestamp. */
+	uint64_t prev_calls;	/**< Previous number of calls. */
+	uint64_t prev_objs;	/**< Previous number of processed objs. */
+	uint64_t prev_cycles;	/**< Previous number of cycles. */
+
+	uint64_t realloc_count; /**< Realloc count. */
+
+	rte_node_t id;	/**< Node identifier of stats. */
+	uint64_t hz;	/**< Cycles per seconds. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+} __rte_cache_aligned;
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure defines the node registration parameters.
+ *
+ * @see __rte_node_register(), RTE_NODE_REGISTER()
+ */
+struct rte_node_register {
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
+	rte_node_process_t process; /**< Node process function. */
+	rte_node_init_t init;       /**< Node init function. */
+	rte_node_fini_t fini;       /**< Node fini function. */
+	rte_node_t id;		    /**< Node Identifier. */
+	rte_node_t parent_id;       /**< Identifier of parent node. */
+	rte_edge_t nb_edges;        /**< Number of edges from this node. */
+	const char *next_nodes[];   /**< Names of next nodes. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create Graph.
+ *
+ * Create memory reel, detect loops and find isolated nodes.
+ *
+ * @param name
+ *   Unique name for this graph.
+ * @param prm
+ *   Graph parameter, includes node names and count to be included
+ *   in this graph.
+ *
+ * @return
+ *   Unique graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy Graph.
+ *
+ * Free Graph memory reel.
+ *
+ * @param name
+ *   Name of the graph to destroy.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_destroy(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph id from graph name.
+ *
+ * @param name
+ *   Name of the graph to get id.
+ *
+ * @return
+ *   Graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph name from graph id.
+ *
+ * @param id
+ *   id of the graph to get name.
+ *
+ * @return
+ *   Graph name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_graph_id_to_name(rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Export the graph as graph viz dot file
+ *
+ * @param name
+ *   Name of the graph to export.
+ * @param f
+ *   File pointer to export the graph.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_export(const char *name, FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph object from its name.
+ *
+ * Typical usage of this API to get graph objects in the worker thread and
+ * followed calling rte_graph_walk() in a loop.
+ *
+ * @param name
+ *   Name of the graph.
+ *
+ * @return
+ *   Graph pointer on success, NULL otherwise.
+ *
+ * @see rte_graph_walk()
+ */
+__rte_experimental
+struct rte_graph *rte_graph_lookup(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum number of graph available.
+ *
+ * @return
+ *   Maximum graph count on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump the graph information to file.
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param id
+ *   Graph id to get graph info.
+ */
+__rte_experimental
+void rte_graph_dump(FILE *f, rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all graphs information to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ */
+__rte_experimental
+void rte_graph_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump graph information along with node info to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param graph
+ *   Graph pointer to get graph info.
+ * @param all
+ *   true to dump nodes in the graph.
+ */
+__rte_experimental
+void rte_graph_obj_dump(FILE *f, struct rte_graph *graph, bool all);
+
+/** Macro to browse rte_node object after the graph creation */
+#define rte_graph_foreach_node(count, off, graph, node)                        \
+	for (count = 0, off = graph->nodes_start,                              \
+	     node = RTE_PTR_ADD(graph, off);                                   \
+	     count < graph->nb_nodes;                                          \
+	     off = node->next, node = RTE_PTR_ADD(graph, off), count++)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node object with in graph from id.
+ *
+ * @param graph_id
+ *   Graph id to get node pointer from.
+ * @param node_id
+ *   Node id to get node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get(rte_graph_t graph_id, uint32_t node_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node pointer with in graph from name.
+ *
+ * @param graph
+ *   Graph name to get node pointer from.
+ * @param name
+ *   Node name to get the node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get_by_name(const char *graph,
+					    const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create graph stats cluster to aggregate runtime node stats.
+ *
+ * @param prm
+ *   Parameters including file pointer to dump stats,
+ *   Graph pattern to create cluster and callback function.
+ *
+ * @return
+ *   Valid pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_graph_cluster_stats *rte_graph_cluster_stats_create(
+			const struct rte_graph_cluster_stats_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy cluster stats.
+ *
+ * @param stat
+ *    Valid cluster pointer to destroy.
+ *
+ */
+__rte_experimental
+void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get stats to application.
+ *
+ * @param[out] stat
+ *   Cluster status.
+ * @param skip_cb
+ *   true to skip callback function invocation.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat,
+				 bool skip_cb);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Reset cluster stats to zero.
+ *
+ * @param stat
+ *   Valid cluster stats pointer.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register new packet processing node. Nodes can be registered
+ * dynamically via this call or statically via the RTE_NODE_REGISTER
+ * macro.
+ *
+ * @param node
+ *   Valid node pointer with name, process function and next_nodes.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ *
+ * @see RTE_NODE_REGISTER()
+ */
+__rte_experimental
+rte_node_t __rte_node_register(const struct rte_node_register *node);
+
+/**
+ * Register a static node.
+ *
+ * The static node is registered through the constructor scheme, thereby, it can
+ * be used in a multi-process scenario.
+ *
+ * @param node
+ *   Valid node pointer with name, process function, and next_nodes.
+ */
+#define RTE_NODE_REGISTER(node)                                                \
+	RTE_INIT(rte_node_register_##node)                                     \
+	{                                                                      \
+		node.parent_id = RTE_NODE_ID_INVALID;                          \
+		node.id = __rte_node_register(&node);                          \
+	}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Clone a node from static node(node created from RTE_NODE_REGISTER).
+ *
+ * @param id
+ *   Static node id to clone from.
+ * @param name
+ *   Name of the new node. The library prepends the parent node name to the
+ * user-specified the name. The final node name will be,
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_clone(rte_node_t id, const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node id from node name.
+ *
+ * @param name
+ *   Valid node name. In the case of the cloned node, the name will be
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node name from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid node name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_node_id_to_name(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the number of edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_count(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Update the edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ * @param from
+ *   Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
+ * in that case, it will be added to the end of the list.
+ * @param next_nodes
+ *   Name of the edges to update.
+ * @param nb_edges
+ *   Number of edges to update.
+ *
+ * @return
+ *   Valid edge count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
+				const char **next_nodes, uint16_t nb_edges);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Shrink the edges to a given size.
+ *
+ * @param id
+ *   Valid node id.
+ * @param size
+ *   New size to shrink the edges.
+ *
+ * @return
+ *   New size on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the edge names from a given node.
+ *
+ * @param id
+ *   Valid node id.
+ * @param[out] next_nodes
+ *   Buffer to copy the edge names. The NULL value is allowed in that case,
+ * the function returns the size of the array that needs to be allocated.
+ *
+ * @return
+ *   When next_nodes == NULL, it returns the size of the array else
+ *  number of item copied.
+ */
+__rte_experimental
+rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum nodes created so far.
+ *
+ * @return
+ *   Maximum nodes count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ * @param id
+ *   Node id to get the info.
+ */
+__rte_experimental
+void rte_node_dump(FILE *f, rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ */
+__rte_experimental
+void rte_node_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of node id.
+ *
+ * @param id
+ *   Node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_node_is_invalid(rte_node_t id)
+{
+	return (id == RTE_NODE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of edge id.
+ *
+ * @param id
+ *   Edge node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_edge_is_invalid(rte_edge_t id)
+{
+	return (id == RTE_EDGE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of graph id.
+ *
+ * @param id
+ *   Graph id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_is_invalid(rte_graph_t id)
+{
+	return (id == RTE_GRAPH_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test stats feature support.
+ *
+ * @return
+ *   1 if stats enabled, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_has_stats_feature(void)
+{
+#ifdef RTE_LIBRTE_GRAPH_STATS
+	return RTE_LIBRTE_GRAPH_STATS;
+#else
+	return 0;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_H_ */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
new file mode 100644
index 000000000..55ef35df5
--- /dev/null
+++ b/lib/librte_graph/rte_graph_version.map
@@ -0,0 +1,3 @@
+EXPERIMENTAL {
+
+};
diff --git a/lib/meson.build b/lib/meson.build
index 9c3cc55d5..c43d86bb9 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index d295ca0a5..b1195f09a 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -98,6 +98,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
+_LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 02/28] graph: implement node registration
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 01/28] graph: define the public API for graph support jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 03/28] graph: implement node operations jerinj
                     ` (27 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding rte_node_register() API implementation includes allocating
memory for node object, check for duplicate node name and
add the allocated node to STAILQ node_list for future use.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph.c               |  18 +++-
 lib/librte_graph/graph_private.h       |  75 ++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/node.c                | 115 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   4 +
 6 files changed, 213 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/node.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 26fe514f3..933d0ee49 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -14,6 +14,7 @@ LDLIBS += -lrte_eal
 EXPORT_MAP := rte_graph_version.map
 
 # all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a55bf443a..a9c124896 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,4 +2,20 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
-#include "rte_graph.h"
+#include <rte_spinlock.h>
+
+#include "graph_private.h"
+
+static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+
+void
+graph_spinlock_lock(void)
+{
+	rte_spinlock_lock(&graph_lock);
+}
+
+void
+graph_spinlock_unlock(void)
+{
+	rte_spinlock_unlock(&graph_lock);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
new file mode 100644
index 000000000..8b9ff5292
--- /dev/null
+++ b/lib/librte_graph/graph_private.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_PRIVATE_H_
+#define _RTE_GRAPH_PRIVATE_H_
+
+#include <inttypes.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_eal.h>
+
+#include "rte_graph.h"
+
+/**
+ * @internal
+ *
+ * Structure that holds node internal data.
+ */
+struct node {
+	STAILQ_ENTRY(node) next;      /**< Next node in the list. */
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+	rte_node_process_t process;   /**< Node process function. */
+	rte_node_init_t init;         /**< Node init function. */
+	rte_node_fini_t fini;	      /**< Node fini function. */
+	rte_node_t id;		      /**< Allocated identifier for the node. */
+	rte_node_t parent_id;	      /**< Parent node identifier. */
+	rte_edge_t nb_edges;	      /**< Number of edges from this node. */
+	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
+};
+
+/* Node functions */
+STAILQ_HEAD(node_head, node);
+
+/**
+ * @internal
+ *
+ * Get the head of the node list.
+ *
+ * @return
+ *   Pointer to the node head.
+ */
+struct node_head *node_list_head_get(void);
+
+/**
+ * @internal
+ *
+ * Get node pointer from node name.
+ *
+ * @param name
+ *   Pointer to character string containing the node name.
+ *
+ * @return
+ *   Pointer to the node.
+ */
+struct node *node_from_name(const char *name);
+
+/* Lock functions */
+/**
+ * @internal
+ *
+ * Take a lock on the graph internal spin lock.
+ */
+void graph_spinlock_lock(void);
+
+/**
+ * @internal
+ *
+ * Release a lock on the graph internal spin lock.
+ */
+void graph_spinlock_unlock(void);
+
+#endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 455cf2ba5..5754ac23b 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('graph.c')
+sources = files('node.c', 'graph.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
new file mode 100644
index 000000000..7999ca6ed
--- /dev/null
+++ b/lib/librte_graph/node.c
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_string_fns.h>
+
+#include "graph_private.h"
+
+static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
+static rte_node_t node_id;
+
+#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)
+
+/* Private functions */
+struct node_head *
+node_list_head_get(void)
+{
+	return &node_list;
+}
+
+struct node *
+node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static bool
+node_has_duplicate_entry(const char *name)
+{
+	struct node *node;
+
+	/* Is duplicate name registered */
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0) {
+			rte_errno = EEXIST;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Public functions */
+rte_node_t
+__rte_node_register(const struct rte_node_register *reg)
+{
+	struct node *node;
+	rte_edge_t i;
+	size_t sz;
+
+	graph_spinlock_lock();
+
+	/* Check sanity */
+	if (reg == NULL || reg->process == NULL) {
+		rte_errno = EINVAL;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(reg->name))
+		goto fail;
+
+	sz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);
+	node = calloc(1, sz);
+	if (node == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Initialize the node */
+	if (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {
+		rte_errno = E2BIG;
+		goto free;
+	}
+	node->flags = reg->flags;
+	node->process = reg->process;
+	node->init = reg->init;
+	node->fini = reg->fini;
+	node->nb_edges = reg->nb_edges;
+	node->parent_id = reg->parent_id;
+	for (i = 0; i < reg->nb_edges; i++) {
+		if (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto free;
+		}
+	}
+
+	node->id = node_id++;
+
+	/* Add the node at tail */
+	STAILQ_INSERT_TAIL(&node_list, node, next);
+	graph_spinlock_unlock();
+
+	return node->id;
+free:
+	free(node);
+fail:
+	graph_spinlock_unlock();
+	return RTE_NODE_ID_INVALID;
+}
+
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 55ef35df5..0884c09f1 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -1,3 +1,7 @@
 EXPERIMENTAL {
+	global:
 
+	__rte_node_register;
+
+	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 03/28] graph: implement node operations
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 01/28] graph: define the public API for graph support jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 02/28] graph: implement node registration jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 04/28] graph: implement node debug routines jerinj
                     ` (26 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding node-specific API implementation like cloning node, updating
edges for the node, shrinking edges of a node, retrieving edges of a
node.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph_private.h       |  10 +
 lib/librte_graph/node.c                | 269 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  10 +
 3 files changed, 289 insertions(+)

diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 8b9ff5292..7ed6d01b6 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+
+#define ID_CHECK(id, id_max)                                                   \
+	do {                                                                   \
+		if ((id) >= (id_max)) {                                        \
+			rte_errno = EINVAL;                                    \
+			goto fail;                                             \
+		}                                                              \
+	} while (0)
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 7999ca6ed..8de857889 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -113,3 +113,272 @@ __rte_node_register(const struct rte_node_register *reg)
 	return RTE_NODE_ID_INVALID;
 }
 
+static int
+clone_name(struct rte_node_register *reg, struct node *node, const char *name)
+{
+	ssize_t sz, rc;
+
+#define SZ RTE_NODE_NAMESIZE
+	rc = rte_strscpy(reg->name, node->name, SZ);
+	if (rc < 0)
+		goto fail;
+	sz = rc;
+	rc = rte_strscpy(reg->name + sz, "-", RTE_MAX((int16_t)(SZ - sz), 0));
+	if (rc < 0)
+		goto fail;
+	sz += rc;
+	sz = rte_strscpy(reg->name + sz, name, RTE_MAX((int16_t)(SZ - sz), 0));
+	if (sz < 0)
+		goto fail;
+
+	return 0;
+fail:
+	rte_errno = E2BIG;
+	return -rte_errno;
+}
+
+static rte_node_t
+node_clone(struct node *node, const char *name)
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct rte_node_register *reg;
+	rte_edge_t i;
+
+	/* Don't allow to clone a node from a cloned node */
+	if (node->parent_id != RTE_NODE_ID_INVALID) {
+		rte_errno = EEXIST;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(name))
+		goto fail;
+
+	reg = calloc(1, sizeof(*reg) + (sizeof(char *) * node->nb_edges));
+	if (reg == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Clone the source node */
+	reg->flags = node->flags;
+	reg->process = node->process;
+	reg->init = node->init;
+	reg->fini = node->fini;
+	reg->nb_edges = node->nb_edges;
+	reg->parent_id = node->id;
+
+	for (i = 0; i < node->nb_edges; i++)
+		reg->next_nodes[i] = node->next_nodes[i];
+
+	/* Naming ceremony of the new node. name is node->name + "-" + name */
+	if (clone_name(reg, node, name))
+		goto free;
+
+	rc = __rte_node_register(reg);
+free:
+	free(reg);
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_clone(rte_node_t id, const char *name)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node_clone(node, name);
+
+fail:
+	return RTE_NODE_ID_INVALID;
+}
+
+rte_node_t
+rte_node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node->id;
+
+	return RTE_NODE_ID_INVALID;
+}
+
+char *
+rte_node_id_to_name(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->name;
+
+fail:
+	return NULL;
+}
+
+rte_edge_t
+rte_node_edge_count(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->nb_edges;
+fail:
+	return RTE_EDGE_ID_INVALID;
+}
+
+static rte_edge_t
+edge_update(struct node *node, struct node *prev, rte_edge_t from,
+	    const char **next_nodes, rte_edge_t nb_edges)
+{
+	rte_edge_t i, max_edges, count = 0;
+	struct node *new_node;
+	bool need_realloc;
+	size_t sz;
+
+	if (from == RTE_EDGE_ID_INVALID)
+		from = node->nb_edges;
+
+	/* Don't create hole in next_nodes[] list */
+	if (from > node->nb_edges) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Remove me from list */
+	STAILQ_REMOVE(&node_list, node, node, next);
+
+	/* Allocate the storage space for new node if required */
+	max_edges = from + nb_edges;
+	need_realloc = max_edges > node->nb_edges;
+	if (need_realloc) {
+		sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
+		new_node = realloc(node, sz);
+		if (new_node == NULL) {
+			rte_errno = ENOMEM;
+			goto restore;
+		} else {
+			node = new_node;
+		}
+	}
+
+	/* Update the new nodes name */
+	for (i = from; i < max_edges; i++, count++) {
+		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto restore;
+		}
+	}
+restore:
+	/* Update the linked list to point new node address in prev node */
+	if (prev)
+		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
+	else
+		STAILQ_INSERT_HEAD(&node_list, node, next);
+
+	if (need_realloc)
+		node->nb_edges += count;
+
+fail:
+	return count;
+}
+
+rte_edge_t
+rte_node_edge_shrink(rte_node_t id, rte_edge_t size)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (node->nb_edges < size) {
+				rte_errno = E2BIG;
+				goto fail;
+			}
+			node->nb_edges = size;
+			rc = size;
+			break;
+		}
+	}
+
+fail:
+	graph_spinlock_unlock();
+	return rc;
+}
+
+rte_edge_t
+rte_node_edge_update(rte_node_t id, rte_edge_t from, const char **next_nodes,
+		     uint16_t nb_edges)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *n, *prev;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	prev = NULL;
+	STAILQ_FOREACH(n, &node_list, next) {
+		if (n->id == id) {
+			rc = edge_update(n, prev, from, next_nodes, nb_edges);
+			break;
+		}
+		prev = n;
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+static rte_node_t
+node_copy_edges(struct node *node, char *next_nodes[])
+{
+	rte_edge_t i;
+
+	for (i = 0; i < node->nb_edges; i++)
+		next_nodes[i] = node->next_nodes[i];
+
+	return i;
+}
+
+rte_node_t
+rte_node_edge_get(rte_node_t id, char *next_nodes[])
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (next_nodes == NULL)
+				rc = sizeof(char *) * node->nb_edges;
+			else
+				rc = node_copy_edges(node, next_nodes);
+			break;
+		}
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_max_count(void)
+{
+	return node_id;
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 0884c09f1..412386356 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,5 +3,15 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 
+	rte_node_clone;
+	rte_node_edge_count;
+	rte_node_edge_get;
+	rte_node_edge_shrink;
+	rte_node_edge_update;
+	rte_node_from_name;
+	rte_node_id_to_name;
+	rte_node_list_dump;
+	rte_node_max_count;
+
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 04/28] graph: implement node debug routines
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (2 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 03/28] graph: implement node operations jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 05/28] graph: implement internal graph operation helpers jerinj
                     ` (25 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding node debug API implementation support to dump
single or all the node objects to the given file.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/Makefile              |  1 +
 lib/librte_graph/graph_debug.c         | 25 ++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 12 ++++++++++
 lib/librte_graph/meson.build           |  2 +-
 lib/librte_graph/node.c                | 32 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 6 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_debug.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 933d0ee49..2a6d86933 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
new file mode 100644
index 000000000..75238e7ca
--- /dev/null
+++ b/lib/librte_graph/graph_debug.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_debug.h>
+
+#include "graph_private.h"
+
+void
+node_dump(FILE *f, struct node *n)
+{
+	rte_edge_t i;
+
+	fprintf(f, "node <%s>\n", n->name);
+	fprintf(f, "  id=%" PRIu32 "\n", n->id);
+	fprintf(f, "  flags=0x%" PRIx64 "\n", n->flags);
+	fprintf(f, "  addr=%p\n", n);
+	fprintf(f, "  process=%p\n", n->process);
+	fprintf(f, "  nb_edges=%d\n", n->nb_edges);
+
+	for (i = 0; i < n->nb_edges; i++)
+		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
+}
+
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7ed6d01b6..6db04cee7 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -82,4 +82,16 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/**
+ * @internal
+ *
+ * Dump internal node object data.
+ *
+ * @param f
+ *   FILE pointer to dump the info.
+ * @param g
+ *   Pointer to the internal node object.
+ */
+void node_dump(FILE *f, struct node *n);
+
 #endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 5754ac23b..01512182f 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c')
+sources = files('node.c', 'graph.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 8de857889..2f9c2ea4c 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
 	return rc;
 }
 
+static void
+node_scan_dump(FILE *f, rte_node_t id, bool all)
+{
+	struct node *node;
+
+	RTE_ASSERT(f != NULL);
+	NODE_ID_CHECK(id);
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (all == true) {
+			node_dump(f, node);
+		} else if (node->id == id) {
+			node_dump(f, node);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_node_dump(FILE *f, rte_node_t id)
+{
+	node_scan_dump(f, id, false);
+}
+
+void
+rte_node_list_dump(FILE *f)
+{
+	node_scan_dump(f, 0, true);
+}
+
 rte_node_t
 rte_node_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 412386356..f2c2139c5 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	__rte_node_register;
 
 	rte_node_clone;
+	rte_node_dump;
 	rte_node_edge_count;
 	rte_node_edge_get;
 	rte_node_edge_shrink;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 05/28] graph: implement internal graph operation helpers
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (3 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 04/28] graph: implement node debug routines jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 06/28] graph: populate fastpath memory for graph reel jerinj
                     ` (24 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding internal graph API helpers support to check whether a graph has
isolated nodes and any node have a loop to itself and BFS
algorithm implementation etc.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
---
 lib/librte_graph/Makefile        |   1 +
 lib/librte_graph/graph.c         |   2 +-
 lib/librte_graph/graph_ops.c     | 169 ++++++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h | 173 +++++++++++++++++++++++++++++++
 lib/librte_graph/meson.build     |   2 +-
 5 files changed, 345 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_ops.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 2a6d86933..39ecb2652 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a9c124896..4c3f2fe7b 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -7,7 +7,7 @@
 #include "graph_private.h"
 
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
-
+int rte_graph_logtype;
 void
 graph_spinlock_lock(void)
 {
diff --git a/lib/librte_graph/graph_ops.c b/lib/librte_graph/graph_ops.c
new file mode 100644
index 000000000..335595311
--- /dev/null
+++ b/lib/librte_graph/graph_ops.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+
+#include "graph_private.h"
+
+/* Check whether a node has next_node to itself */
+static inline int
+node_has_loop_edge(struct node *node)
+{
+	rte_edge_t i;
+	char *name;
+	int rc = 0;
+
+	for (i = 0; i < node->nb_edges; i++) {
+		if (strncmp(node->name, node->next_nodes[i],
+			    RTE_NODE_NAMESIZE) == 0) {
+			name = node->name;
+			rc = 1;
+			SET_ERR_JMP(EINVAL, fail, "Node %s has loop to self",
+				    name);
+		}
+	}
+fail:
+	return rc;
+}
+
+int
+graph_node_has_loop_edge(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (node_has_loop_edge(graph_node->node))
+			return 1;
+
+	return 0;
+}
+
+rte_node_t
+graph_src_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t rc = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F)
+			rc++;
+
+	if (rc == 0)
+		SET_ERR_JMP(EINVAL, fail, "Graph needs at least a source node");
+fail:
+	return rc;
+}
+
+/* Check whether a node has next_node to a source node */
+int
+graph_node_has_edge_to_src_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *node;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			node = graph_node->adjacency_list[i]->node;
+			if (node->flags & RTE_NODE_SOURCE_F)
+				SET_ERR_JMP(
+					EEXIST, fail,
+					"Node %s points to the source node %s",
+					graph_node->node->name, node->name);
+		}
+	}
+
+	return 0;
+fail:
+	return 1;
+}
+
+rte_node_t
+graph_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t count = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		count++;
+
+	return count;
+}
+
+void
+graph_mark_nodes_as_not_visited(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		graph_node->visited = false;
+}
+
+int
+graph_bfs(struct graph *graph, struct graph_node *start)
+{
+	struct graph_node **queue, *v, *tmp;
+	uint16_t head = 0, tail = 0;
+	rte_edge_t i;
+	size_t sz;
+
+	sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
+	queue = malloc(sz);
+	if (queue == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
+			    sz);
+
+	/* BFS algorithm */
+	queue[tail++] = start;
+	start->visited = true;
+	while (head != tail) {
+		v = queue[head++];
+		for (i = 0; i < v->node->nb_edges; i++) {
+			tmp = v->adjacency_list[i];
+			if (tmp->visited == false) {
+				queue[tail++] = tmp;
+				tmp->visited = true;
+			}
+		}
+	}
+
+	free(queue);
+	return 0;
+
+fail:
+	return -rte_errno;
+}
+
+/* Check whether a node has connected path or parent node */
+int
+graph_has_isolated_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	graph_mark_nodes_as_not_visited(graph);
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			if (graph_node->node->nb_edges == 0)
+				SET_ERR_JMP(EINVAL, fail,
+					    "%s node needs minimum one edge",
+					    graph_node->node->name);
+			if (graph_bfs(graph, graph_node))
+				goto fail;
+		}
+	}
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->visited == false)
+			SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
+				    graph_node->node->name);
+
+	return 0;
+fail:
+	return 1;
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 6db04cee7..220a35e2a 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+extern int rte_graph_logtype;
+
+#define GRAPH_LOG(level, ...)                                                  \
+	rte_log(RTE_LOG_##level, rte_graph_logtype,                            \
+		RTE_FMT("GRAPH: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",    \
+			__func__, __LINE__, RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define graph_err(...) GRAPH_LOG(ERR, __VA_ARGS__)
+#define graph_info(...) GRAPH_LOG(INFO, __VA_ARGS__)
+#define graph_dbg(...) GRAPH_LOG(DEBUG, __VA_ARGS__)
 
 #define ID_CHECK(id, id_max)                                                   \
 	do {                                                                   \
@@ -22,6 +32,12 @@
 		}                                                              \
 	} while (0)
 
+#define SET_ERR_JMP(err, where, fmt, ...)                                      \
+	do {                                                                   \
+		graph_err(fmt, ##__VA_ARGS__);                                 \
+		rte_errno = err;                                               \
+		goto where;                                                    \
+	} while (0)
 
 /**
  * @internal
@@ -41,6 +57,52 @@ struct node {
 	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
 };
 
+/**
+ * @internal
+ *
+ * Structure that holds the graph node data.
+ */
+struct graph_node {
+	STAILQ_ENTRY(graph_node) next; /**< Next graph node in the list. */
+	struct node *node; /**< Pointer to internal node. */
+	bool visited;      /**< Flag used in BFS to mark node visited. */
+	struct graph_node *adjacency_list[]; /**< Adjacency list of the node. */
+};
+
+/**
+ * @internal
+ *
+ * Structure that holds graph internal data.
+ */
+struct graph {
+	STAILQ_ENTRY(graph) next;
+	/**< List of graphs. */
+	char name[RTE_GRAPH_NAMESIZE];
+	/**< Name of the graph. */
+	const struct rte_memzone *mz;
+	/**< Memzone to store graph data. */
+	rte_graph_off_t nodes_start;
+	/**< Node memory start offset in graph reel. */
+	rte_node_t src_node_count;
+	/**< Number of source nodes in a graph. */
+	struct rte_graph *graph;
+	/**< Pointer to graph data. */
+	rte_node_t node_count;
+	/**< Total number of nodes. */
+	uint32_t cir_start;
+	/**< Circular buffer start offset in graph reel. */
+	uint32_t cir_mask;
+	/**< Circular buffer mask for wrap around. */
+	rte_graph_t id;
+	/**< Graph identifier. */
+	size_t mem_sz;
+	/**< Memory size of the graph. */
+	int socket;
+	/**< Socket identifier where memory is allocated. */
+	STAILQ_HEAD(gnode_list, graph_node) node_list;
+	/**< Nodes in a graph. */
+};
+
 /* Node functions */
 STAILQ_HEAD(node_head, node);
 
@@ -67,6 +129,19 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);
 
+/* Graph list functions */
+STAILQ_HEAD(graph_head, graph);
+
+/**
+ * @internal
+ *
+ * Get the head of the graph list.
+ *
+ * @return
+ *   Pointer to the graph head.
+ */
+struct graph_head *graph_list_head_get(void);
+
 /* Lock functions */
 /**
  * @internal
@@ -82,6 +157,104 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/* Graph operations */
+/**
+ * @internal
+ *
+ * Run a BFS(Breadth First Search) on the graph marking all
+ * the graph nodes as visited.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ * @param start
+ *   Pointer to the starting graph node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough memory for BFS.
+ */
+int graph_bfs(struct graph *graph, struct graph_node *start);
+
+/**
+ * @internal
+ *
+ * Check if there is an isolated node in the given graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: No isolated node found.
+ *   - 1: Isolated node found.
+ */
+int graph_has_isolated_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Check whether a node in the graph has next_node to a source node.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to source node.
+ *   - 1: Node doesn't have an edge to source node.
+ */
+int graph_node_has_edge_to_src_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Checks whether node in the graph has a edge to itself i.e. forms a
+ * loop.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to itself.
+ *   - 1: Node doesn't have an edge to itself.
+ */
+int graph_node_has_loop_edge(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of source nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of source nodes.
+ */
+rte_node_t graph_src_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of total number of nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of nodes.
+ */
+rte_node_t graph_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Clear the visited flag of all the nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ */
+void graph_mark_nodes_as_not_visited(struct graph *graph);
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 01512182f..16e0625c1 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_debug.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 06/28] graph: populate fastpath memory for graph reel
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (4 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 05/28] graph: implement internal graph operation helpers jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 07/28] graph: implement create and destroy APIs jerinj
                     ` (23 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding support to create and populate the memory for graph reel.
This includes reserving the memory in the memzone, populating the nodes,
Allocating memory for node-specific streams to hold objects.

Once it is populated the reel memory contains the following sections.

+---------------------+
|   Graph Header      |
+---------------------+
|   Fence             |
+---------------------+
|   Circular buffer   |
+---------------------+
|   Fence             |
+---------------------+
|   Node Object 0     |
+------------------- -+
|   Node Object 1     |
+------------------- -+
|   Node Object 2     |
+------------------- -+
|   Node Object n     |
+------------------- -+

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/Makefile              |   2 +
 lib/librte_graph/graph.c               |  16 ++
 lib/librte_graph/graph_populate.c      | 234 +++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       |  64 +++++++
 lib/librte_graph/meson.build           |   4 +-
 lib/librte_graph/node.c                |   5 +
 lib/librte_graph/rte_graph_version.map |   1 +
 lib/librte_graph/rte_graph_worker.h    | 108 ++++++++++++
 8 files changed, 432 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/rte_graph_worker.h

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 39ecb2652..7bfd7d51f 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,8 +18,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph_worker.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 4c3f2fe7b..e1930b7d2 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,6 +2,7 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <rte_malloc.h>
 #include <rte_spinlock.h>
 
 #include "graph_private.h"
@@ -19,3 +20,18 @@ graph_spinlock_unlock(void)
 {
 	rte_spinlock_unlock(&graph_lock);
 }
+
+void __rte_noinline
+__rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
diff --git a/lib/librte_graph/graph_populate.c b/lib/librte_graph/graph_populate.c
new file mode 100644
index 000000000..093512efa
--- /dev/null
+++ b/lib/librte_graph/graph_populate.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "graph_private.h"
+
+static size_t
+graph_fp_mem_calc_size(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t val;
+	size_t sz;
+
+	/* Graph header */
+	sz = sizeof(struct rte_graph);
+	/* Source nodes list */
+	sz += sizeof(rte_graph_off_t) * graph->src_node_count;
+	/* Circular buffer for pending streams of size number of nodes */
+	val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
+	sz = RTE_ALIGN(sz, val);
+	graph->cir_start = sz;
+	graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
+	sz += val;
+	/* Fence */
+	sz += sizeof(RTE_GRAPH_FENCE);
+	sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+	graph->nodes_start = sz;
+	/* For 0..N node objects with fence */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+		sz += sizeof(struct rte_node);
+		/* Pointer to next nodes(edges) */
+		sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
+	}
+
+	graph->mem_sz = sz;
+	return sz;
+}
+
+static void
+graph_header_popluate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+
+	graph->tail = 0;
+	graph->head = (int32_t)-_graph->src_node_count;
+	graph->cir_mask = _graph->cir_mask;
+	graph->nb_nodes = _graph->node_count;
+	graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
+	graph->nodes_start = _graph->nodes_start;
+	graph->socket = _graph->socket;
+	graph->id = _graph->id;
+	memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);
+	graph->fence = RTE_GRAPH_FENCE;
+}
+
+static void
+graph_nodes_populate(struct graph *_graph)
+{
+	rte_graph_off_t off = _graph->nodes_start;
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	rte_edge_t count, nb_edges;
+	const char *parent;
+	rte_node_t pid;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		struct rte_node *node = RTE_PTR_ADD(graph, off);
+		memset(node, 0, sizeof(*node));
+		node->fence = RTE_GRAPH_FENCE;
+		node->off = off;
+		node->process = graph_node->node->process;
+		memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
+		pid = graph_node->node->parent_id;
+		if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
+			parent = rte_node_id_to_name(pid);
+			memcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);
+		}
+		node->id = graph_node->node->id;
+		node->parent_id = pid;
+		nb_edges = graph_node->node->nb_edges;
+		node->nb_edges = nb_edges;
+		off += sizeof(struct rte_node);
+		/* Copy the name in first pass to replace with rte_node* later*/
+		for (count = 0; count < nb_edges; count++)
+			node->nodes[count] = (struct rte_node *)&graph_node
+						     ->adjacency_list[count]
+						     ->node->name[0];
+
+		off += sizeof(struct rte_node *) * nb_edges;
+		off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
+		node->next = off;
+		__rte_node_stream_alloc(graph, node);
+	}
+}
+
+struct rte_node *
+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (unlikely(node->id == id))
+			return node;
+
+	return NULL;
+}
+
+struct rte_node *
+graph_node_name_to_ptr(const struct rte_graph *graph, const char *name)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (strncmp(name, node->name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static int
+graph_node_nexts_populate(struct graph *_graph)
+{
+	rte_node_t count, val;
+	rte_graph_off_t off;
+	struct rte_node *node;
+	const struct rte_graph *graph = _graph->graph;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		for (val = 0; val < node->nb_edges; val++) {
+			name = (const char *)node->nodes[val];
+			node->nodes[val] = graph_node_name_to_ptr(graph, name);
+			if (node->nodes[val] == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_src_nodes_populate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	struct rte_node *node;
+	int32_t head = -1;
+	const char *name;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			name = graph_node->node->name;
+			node = graph_node_name_to_ptr(graph, name);
+			if (node == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+
+			__rte_node_stream_alloc(graph, node);
+			graph->cir_start[head--] = node->off;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_fp_mem_populate(struct graph *graph)
+{
+	int rc;
+
+	graph_header_popluate(graph);
+	graph_nodes_populate(graph);
+	rc = graph_node_nexts_populate(graph);
+	rc |= graph_src_nodes_populate(graph);
+
+	return rc;
+}
+
+int
+graph_fp_mem_create(struct graph *graph)
+{
+	const struct rte_memzone *mz;
+	size_t sz;
+
+	sz = graph_fp_mem_calc_size(graph);
+	mz = rte_memzone_reserve(graph->name, sz, graph->socket, 0);
+	if (mz == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Memzone %s reserve failed",
+			    graph->name);
+
+	graph->graph = mz->addr;
+	graph->mz = mz;
+
+	return graph_fp_mem_populate(graph);
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_nodes_mem_destroy(struct rte_graph *graph)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	if (graph == NULL)
+		return;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		rte_free(node->objs);
+}
+
+int
+graph_fp_mem_destroy(struct graph *graph)
+{
+	graph_nodes_mem_destroy(graph->graph);
+	return rte_memzone_free(graph->mz);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 220a35e2a..7fce52e00 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -12,6 +12,7 @@
 #include <rte_eal.h>
 
 #include "rte_graph.h"
+#include "rte_graph_worker.h"
 
 extern int rte_graph_logtype;
 
@@ -254,6 +255,69 @@ rte_node_t graph_nodes_count(struct graph *graph);
  */
 void graph_mark_nodes_as_not_visited(struct graph *graph);
 
+/* Fast path graph memory populate unctions */
+
+/**
+ * @internal
+ *
+ * Create fast-path memory for the graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough for graph and nodes.
+ *   - -EINVAL: Graph nodes not found.
+ */
+int graph_fp_mem_create(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Free fast-path memory used by graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Graph memzone related error.
+ */
+int graph_fp_mem_destroy(struct graph *graph);
+
+/* Lookup functions */
+/**
+ * @internal
+ *
+ * Get graph node object from node id.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param id
+ *   Node Identifier.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
+				      rte_node_t id);
+
+/**
+ * @internal
+ *
+ * Get graph node object from node name.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param node_name
+ *   Pointer to character string holding the node name.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
+					const char *node_name);
 
 /**
  * @internal
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 16e0625c1..fb203a5e2 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,8 +4,8 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
-headers = files('rte_graph.h')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
 deps += ['eal']
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 2f9c2ea4c..639269870 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -61,6 +61,11 @@ __rte_node_register(const struct rte_node_register *reg)
 	rte_edge_t i;
 	size_t sz;
 
+	/* Limit Node specific metadata to one cacheline on 64B CL machine */
+	RTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -
+			  offsetof(struct rte_node, ctx)) !=
+			 RTE_CACHE_LINE_MIN_SIZE);
+
 	graph_spinlock_lock();
 
 	/* Check sanity */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index f2c2139c5..a9fe1b610 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -2,6 +2,7 @@ EXPERIMENTAL {
 	global:
 
 	__rte_node_register;
+	__rte_node_stream_alloc;
 
 	rte_node_clone;
 	rte_node_dump;
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
new file mode 100644
index 000000000..a8133739d
--- /dev/null
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_WORKER_H_
+#define _RTE_GRAPH_WORKER_H_
+
+/**
+ * @file rte_graph_worker.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows a worker thread to walk over a graph and nodes to create,
+ * process, enqueue and move streams of objects to the next nodes.
+ */
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+
+#include "rte_graph.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ *
+ * Data structure to hold graph data.
+ */
+struct rte_graph {
+	uint32_t tail;		     /**< Tail of circular buffer. */
+	uint32_t head;		     /**< Head of circular buffer. */
+	uint32_t cir_mask;	     /**< Circular buffer wrap around mask. */
+	rte_node_t nb_nodes;	     /**< Number of nodes in the graph. */
+	rte_graph_off_t *cir_start;  /**< Pointer to circular buffer. */
+	rte_graph_off_t nodes_start; /**< Offset at which node memory starts. */
+	rte_graph_t id;	/**< Graph identifier. */
+	int socket;	/**< Socket ID where memory is allocated. */
+	char name[RTE_GRAPH_NAMESIZE];	/**< Name of the graph. */
+	uint64_t fence;			/**< Fence. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Data structure to hold node data.
+ */
+struct rte_node {
+	/* Slow path area  */
+	uint64_t fence;		/**< Fence. */
+	rte_graph_off_t next;	/**< Index to next node. */
+	rte_node_t id;		/**< Node identifier. */
+	rte_node_t parent_id;	/**< Parent Node identifier. */
+	rte_edge_t nb_edges;	/**< Number of edges from this node. */
+	uint32_t realloc_count;	/**< Number of times realloced. */
+
+	char parent[RTE_NODE_NAMESIZE];	/**< Parent node name. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+
+	/* Fast path area  */
+#define RTE_NODE_CTX_SZ 16
+	uint8_t ctx[RTE_NODE_CTX_SZ] __rte_cache_aligned; /**< Node Context. */
+	uint16_t size;		/**< Total number of objects available. */
+	uint16_t idx;		/**< Number of objects used. */
+	rte_graph_off_t off;	/**< Offset of node in the graph reel. */
+	uint64_t total_cycles;	/**< Cycles spent in this node. */
+	uint64_t total_calls;	/**< Calls done to this node. */
+	uint64_t total_objs;	/**< Objects processed by this node. */
+	RTE_STD_C11
+		union {
+			void **objs;	   /**< Array of object pointers. */
+			uint64_t objs_u64;
+		};
+	RTE_STD_C11
+		union {
+			rte_node_process_t process; /**< Process function. */
+			uint64_t process_u64;
+		};
+	struct rte_node *nodes[] __rte_cache_min_aligned; /**< Next nodes. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ */
+__rte_experimental
+void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_WORKER_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 07/28] graph: implement create and destroy APIs
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (5 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 06/28] graph: populate fastpath memory for graph reel jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 08/28] graph: implement graph operation APIs jerinj
                     ` (22 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding graph specific API implementations like graph create
and graph destroy. This detect loops in the graph,
check for isolated nodes and operation to verify the validity of
graph.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 320 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   2 +
 2 files changed, 322 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e1930b7d2..dc373231e 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,13 +2,33 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_errno.h>
 #include <rte_malloc.h>
+#include <rte_memzone.h>
 #include <rte_spinlock.h>
+#include <rte_string_fns.h>
 
 #include "graph_private.h"
 
+static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+static rte_graph_t graph_id;
 int rte_graph_logtype;
+
+#define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id)
+
+/* Private functions */
+struct graph_head *
+graph_list_head_get(void)
+{
+	return &graph_list;
+}
+
 void
 graph_spinlock_lock(void)
 {
@@ -21,6 +41,306 @@ graph_spinlock_unlock(void)
 	rte_spinlock_unlock(&graph_lock);
 }
 
+static int
+graph_node_add(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+	size_t sz;
+
+	/* Skip the duplicate nodes */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (strncmp(node->name, graph_node->node->name,
+			    RTE_NODE_NAMESIZE) == 0)
+			return 0;
+
+	/* Allocate new graph node object */
+	sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);
+	graph_node = calloc(1, sz);
+
+	if (graph_node == NULL)
+		SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object",
+			    node->name);
+
+	/* Initialize the graph node */
+	graph_node->node = node;
+
+	/* Add to graph node list */
+	STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);
+	return 0;
+
+free:
+	free(graph_node);
+	return -rte_errno;
+}
+
+static struct graph_node *
+node_to_graph_node(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node == node)
+			return graph_node;
+
+	SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name);
+fail:
+	return NULL;
+}
+
+static int
+graph_node_edges_add(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			if (graph_node_add(graph, adjacency))
+				goto fail;
+		}
+	}
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_adjacency_list_update(struct graph *graph)
+{
+	struct graph_node *graph_node, *tmp;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			tmp = node_to_graph_node(graph, adjacency);
+			if (tmp == NULL)
+				goto fail;
+			graph_node->adjacency_list[i] = tmp;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+expand_pattern_to_node(struct graph *graph, const char *pattern)
+{
+	struct node_head *node_head = node_list_head_get();
+	bool found = false;
+	struct node *node;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(node, node_head, next) {
+		if (fnmatch(pattern, node->name, 0) == 0) {
+			if (graph_node_add(graph, node))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_cleanup(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	while (!STAILQ_EMPTY(&graph->node_list)) {
+		graph_node = STAILQ_FIRST(&graph->node_list);
+		STAILQ_REMOVE_HEAD(&graph->node_list, next);
+		free(graph_node);
+	}
+}
+
+static int
+graph_node_init(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	const char *name;
+	int rc;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->init) {
+			name = graph_node->node->name;
+			rc = graph_node->node->init(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph, name));
+			if (rc)
+				SET_ERR_JMP(rc, err, "Node %s init() failed",
+					    name);
+		}
+	}
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+graph_node_fini(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->fini)
+			graph_node->node->fini(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph,
+						       graph_node->node->name));
+}
+
+rte_graph_t
+rte_graph_create(const char *name, struct rte_graph_param *prm)
+{
+	struct graph *graph;
+	const char *pattern;
+	uint16_t i;
+
+	graph_spinlock_lock();
+
+	/* Check arguments sanity */
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
+
+	if (name == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
+
+	/* Check for existence of duplicate graph */
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
+			SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
+				    name);
+
+	/* Create graph object */
+	graph = calloc(1, sizeof(*graph));
+	if (graph == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
+
+	/* Initialize the graph object */
+	STAILQ_INIT(&graph->node_list);
+	if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
+		SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
+
+	/* Expand node pattern and add the nodes to the graph */
+	for (i = 0; i < prm->nb_node_patterns; i++) {
+		pattern = prm->node_patterns[i];
+		if (expand_pattern_to_node(graph, pattern))
+			goto graph_cleanup;
+	}
+
+	/* Go over all the nodes edges and add them to the graph */
+	if (graph_node_edges_add(graph))
+		goto graph_cleanup;
+
+	/* Update adjacency list of all nodes in the graph */
+	if (graph_adjacency_list_update(graph))
+		goto graph_cleanup;
+
+	/* Make sure at least a source node present in the graph */
+	if (!graph_src_nodes_count(graph))
+		goto graph_cleanup;
+
+	/* Make sure no node is pointing to source node */
+	if (graph_node_has_edge_to_src_node(graph))
+		goto graph_cleanup;
+
+	/* Don't allow node has loop to self */
+	if (graph_node_has_loop_edge(graph))
+		goto graph_cleanup;
+
+	/* Do BFS from src nodes on the graph to find isolated nodes */
+	if (graph_has_isolated_node(graph))
+		goto graph_cleanup;
+
+	/* Initialize graph object */
+	graph->socket = prm->socket_id;
+	graph->src_node_count = graph_src_nodes_count(graph);
+	graph->node_count = graph_nodes_count(graph);
+	graph->id = graph_id;
+
+	/* Allocate the Graph fast path memory and populate the data */
+	if (graph_fp_mem_create(graph))
+		goto graph_cleanup;
+
+	/* Call init() of the all the nodes in the graph */
+	if (graph_node_init(graph))
+		goto graph_mem_destroy;
+
+	/* All good, Lets add the graph to the list */
+	graph_id++;
+	STAILQ_INSERT_TAIL(&graph_list, graph, next);
+
+	graph_spinlock_unlock();
+	return graph->id;
+
+graph_mem_destroy:
+	graph_fp_mem_destroy(graph);
+graph_cleanup:
+	graph_cleanup(graph);
+free:
+	free(graph);
+fail:
+	graph_spinlock_unlock();
+	return RTE_GRAPH_ID_INVALID;
+}
+
+rte_graph_t
+rte_graph_destroy(const char *graph_name)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph, *tmp;
+	const char *name;
+
+	graph_spinlock_lock();
+
+	graph = STAILQ_FIRST(&graph_list);
+	while (graph != NULL) {
+		tmp = STAILQ_NEXT(graph, next);
+		name = graph->name;
+		if (strncmp(name, graph_name, RTE_GRAPH_NAMESIZE) == 0) {
+			/* Call fini() of the all the nodes in the graph */
+			graph_node_fini(graph);
+			/* Destroy graph fast path memory */
+			rc = graph_fp_mem_destroy(graph);
+			if (rc)
+				SET_ERR_JMP(rc, done, "MZ %s free failed",
+					    name);
+
+			graph_cleanup(graph);
+			STAILQ_REMOVE(&graph_list, graph, graph, next);
+			rc = graph->id;
+			free(graph);
+			graph_id--;
+			goto done;
+		}
+		graph = tmp;
+	}
+done:
+	graph_spinlock_unlock();
+	return rc;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index a9fe1b610..dcbd78c02 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,8 @@ EXPERIMENTAL {
 	__rte_node_register;
 	__rte_node_stream_alloc;
 
+	rte_graph_create;
+	rte_graph_destroy;
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 08/28] graph: implement graph operation APIs
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (6 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 07/28] graph: implement create and destroy APIs jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 09/28] graph: implement Graphviz export jerinj
                     ` (21 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K, Anatoly Burakov
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding support for graph specific API implementation like
Graph lookup to get graph object, retrieving graph ID
From name and graph name from ID.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 131 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   8 ++
 2 files changed, 139 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index dc373231e..7c6a7897d 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -210,6 +210,54 @@ graph_node_fini(struct graph *graph)
 						       graph_node->node->name));
 }
 
+static struct rte_graph *
+graph_mem_fixup_node_ctx(struct rte_graph *graph)
+{
+	struct rte_node *node;
+	struct node *node_db;
+	rte_graph_off_t off;
+	rte_node_t count;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
+			name = node->name;
+		else /* Cloned node */
+			name = node->parent;
+
+		node_db = node_from_name(name);
+		if (node_db == NULL)
+			SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
+		node->process = node_db->process;
+	}
+
+	return graph;
+fail:
+	return NULL;
+}
+
+static struct rte_graph *
+graph_mem_fixup_secondray(struct rte_graph *graph)
+{
+	if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
+		return graph;
+
+	return graph_mem_fixup_node_ctx(graph);
+}
+
+struct rte_graph *
+rte_graph_lookup(const char *name)
+{
+	const struct rte_memzone *mz;
+	struct rte_graph *rc = NULL;
+
+	mz = rte_memzone_lookup(name);
+	if (mz)
+		rc = mz->addr;
+
+	return graph_mem_fixup_secondray(rc);
+}
+
 rte_graph_t
 rte_graph_create(const char *name, struct rte_graph_param *prm)
 {
@@ -341,6 +389,76 @@ rte_graph_destroy(const char *graph_name)
 	return rc;
 }
 
+rte_graph_t
+rte_graph_from_name(const char *name)
+{
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)
+			return graph->id;
+
+	return RTE_GRAPH_ID_INVALID;
+}
+
+char *
+rte_graph_id_to_name(rte_graph_t id)
+{
+	struct graph *graph;
+
+	GRAPH_ID_CHECK(id);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == id)
+			return graph->name;
+
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get(rte_graph_t gid, uint32_t nid)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	GRAPH_ID_CHECK(gid);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == gid) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (node->id == nid)
+					return node;
+			}
+			break;
+		}
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get_by_name(const char *graph_name, const char *node_name)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (!strncmp(node->name, node_name,
+					     RTE_NODE_NAMESIZE))
+					return node;
+			}
+			break;
+		}
+
+	return NULL;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
@@ -355,3 +473,16 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->size = size;
 	node->realloc_count++;
 }
+
+rte_graph_t
+rte_graph_max_count(void)
+{
+	return graph_id;
+}
+
+RTE_INIT(rte_graph_init_log)
+{
+	rte_graph_logtype = rte_log_register("lib.graph");
+	if (rte_graph_logtype >= 0)
+		rte_log_set_level(rte_graph_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index dcbd78c02..5a2b13293 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,14 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_from_name;
+	rte_graph_id_to_name;
+	rte_graph_lookup;
+	rte_graph_list_dump;
+	rte_graph_max_count;
+	rte_graph_node_get;
+	rte_graph_node_get_by_name;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 09/28] graph: implement Graphviz export
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (7 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 08/28] graph: implement graph operation APIs jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 10/28] graph: implement debug routines jerinj
                     ` (20 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding API implementation support exporting the graph object to file.
This will export the graph to a file in Graphviz format.
It can be viewed in many viewers such as
https://dreampuf.github.io/GraphvizOnline/

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 54 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 2 files changed, 55 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 7c6a7897d..e0c0b71a7 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -474,6 +474,60 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+static int
+graph_to_dot(FILE *f, struct graph *graph)
+{
+	const char *src_edge_color = " [color=blue]\n";
+	const char *edge_color = "\n";
+	struct graph_node *graph_node;
+	char *node_name;
+	rte_edge_t i;
+	int rc;
+
+	rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name);
+	if (rc < 0)
+		goto end;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		node_name = graph_node->node->name;
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name,
+				     graph_node->adjacency_list[i]->node->name,
+				     graph_node->node->flags & RTE_NODE_SOURCE_F
+					     ? src_edge_color
+					     : edge_color);
+			if (rc < 0)
+				goto end;
+		}
+	}
+	rc = fprintf(f, "}\n");
+	if (rc < 0)
+		goto end;
+
+	return 0;
+end:
+	rte_errno = EBADF;
+	return -rte_errno;
+}
+
+rte_graph_t
+rte_graph_export(const char *name, FILE *f)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {
+			rc = graph_to_dot(f, graph);
+			goto end;
+		}
+	}
+	rte_errno = ENOENT;
+end:
+	return rc;
+}
+
+
 rte_graph_t
 rte_graph_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 5a2b13293..2797be044 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
 	rte_graph_lookup;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 10/28] graph: implement debug routines
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (8 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 09/28] graph: implement Graphviz export jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 11/28] graph: implement stats support jerinj
                     ` (19 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph specific API to dump the
Graph information to a file. This API will dump detailed internal
info about node objects and graph objects.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_graph/graph.c               | 31 ++++++++++++++
 lib/librte_graph/graph_debug.c         | 59 ++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 13 ++++++
 lib/librte_graph/rte_graph_version.map |  2 +
 4 files changed, 105 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e0c0b71a7..e96363777 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -527,6 +527,37 @@ rte_graph_export(const char *name, FILE *f)
 	return rc;
 }
 
+static void
+graph_scan_dump(FILE *f, rte_graph_t id, bool all)
+{
+	struct graph *graph;
+
+	RTE_VERIFY(f);
+	GRAPH_ID_CHECK(id);
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (all == true) {
+			graph_dump(f, graph);
+		} else if (graph->id == id) {
+			graph_dump(f, graph);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_graph_dump(FILE *f, rte_graph_t id)
+{
+	graph_scan_dump(f, id, false);
+}
+
+void
+rte_graph_list_dump(FILE *f)
+{
+	graph_scan_dump(f, 0, true);
+}
 
 rte_graph_t
 rte_graph_max_count(void)
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
index 75238e7ca..f8aea16ac 100644
--- a/lib/librte_graph/graph_debug.c
+++ b/lib/librte_graph/graph_debug.c
@@ -7,6 +7,26 @@
 
 #include "graph_private.h"
 
+void
+graph_dump(FILE *f, struct graph *g)
+{
+	struct graph_node *graph_node;
+	rte_edge_t i = 0;
+
+	fprintf(f, "graph <%s>\n", g->name);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  cir_start=%" PRIu32 "\n", g->cir_start);
+	fprintf(f, "  cir_mask=%" PRIu32 "\n", g->cir_mask);
+	fprintf(f, "  addr=%p\n", g);
+	fprintf(f, "  graph=%p\n", g->graph);
+	fprintf(f, "  mem_sz=%zu\n", g->mem_sz);
+	fprintf(f, "  node_count=%" PRIu32 "\n", g->node_count);
+	fprintf(f, "  src_node_count=%" PRIu32 "\n", g->src_node_count);
+
+	STAILQ_FOREACH(graph_node, &g->node_list, next)
+		fprintf(f, "     node[%d] <%s>\n", i++, graph_node->node->name);
+}
+
 void
 node_dump(FILE *f, struct node *n)
 {
@@ -23,3 +43,42 @@ node_dump(FILE *f, struct node *n)
 		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
 }
 
+void
+rte_graph_obj_dump(FILE *f, struct rte_graph *g, bool all)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *n;
+	rte_edge_t i;
+
+	fprintf(f, "graph <%s> @ %p\n", g->name, g);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  head=%" PRId32 "\n", (int32_t)g->head);
+	fprintf(f, "  tail=%" PRId32 "\n", (int32_t)g->tail);
+	fprintf(f, "  cir_mask=0x%" PRIx32 "\n", g->cir_mask);
+	fprintf(f, "  nb_nodes=%" PRId32 "\n", g->nb_nodes);
+	fprintf(f, "  socket=%d\n", g->socket);
+	fprintf(f, "  fence=0x%" PRIx64 "\n", g->fence);
+	fprintf(f, "  nodes_start=0x%" PRIx32 "\n", g->nodes_start);
+	fprintf(f, "  cir_start=%p\n", g->cir_start);
+
+	rte_graph_foreach_node(count, off, g, n) {
+		if (!all && n->idx == 0)
+			continue;
+		fprintf(f, "     node[%d] <%s>\n", count, n->name);
+		fprintf(f, "       fence=0x%" PRIx64 "\n", n->fence);
+		fprintf(f, "       objs=%p\n", n->objs);
+		fprintf(f, "       process=%p\n", n->process);
+		fprintf(f, "       id=0x%" PRIx32 "\n", n->id);
+		fprintf(f, "       offset=0x%" PRIx32 "\n", n->off);
+		fprintf(f, "       nb_edges=%" PRId32 "\n", n->nb_edges);
+		fprintf(f, "       realloc_count=%d\n", n->realloc_count);
+		fprintf(f, "       size=%d\n", n->size);
+		fprintf(f, "       idx=%d\n", n->idx);
+		fprintf(f, "       total_objs=%" PRId64 "\n", n->total_objs);
+		fprintf(f, "       total_calls=%" PRId64 "\n", n->total_calls);
+		for (i = 0; i < n->nb_edges; i++)
+			fprintf(f, "          edge[%d] <%s>\n", i,
+				n->nodes[i]->name);
+	}
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7fce52e00..f9a85c892 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -319,6 +319,19 @@ struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
 struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
 					const char *node_name);
 
+/* Debug functions */
+/**
+ * @internal
+ *
+ * Dump internal graph object data.
+ *
+ * @param f
+ *   FILE pointer to dump the data.
+ * @param g
+ *   Pointer to the internal graph object.
+ */
+void graph_dump(FILE *f, struct graph *g);
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 2797be044..851f4772e 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_dump;
 	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
@@ -14,6 +15,7 @@ EXPERIMENTAL {
 	rte_graph_max_count;
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
+	rte_graph_obj_dump;
 
 	rte_node_clone;
 	rte_node_dump;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 11/28] graph: implement stats support
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (9 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 10/28] graph: implement debug routines jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 12/28] graph: implement fastpath API routines jerinj
                     ` (18 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph stats collection API. This API will
create a cluster for a specified node pattern and aggregate the node
runtime stats.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph_stats.c         | 406 +++++++++++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/rte_graph_version.map |   5 +
 4 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_stats.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 7bfd7d51f..967c8d9bc 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,6 +18,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_stats.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
diff --git a/lib/librte_graph/graph_stats.c b/lib/librte_graph/graph_stats.c
new file mode 100644
index 000000000..125e08d73
--- /dev/null
+++ b/lib/librte_graph/graph_stats.c
@@ -0,0 +1,406 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+
+#include "graph_private.h"
+
+/* Capture all graphs of cluster */
+struct cluster {
+	rte_graph_t nb_graphs;
+	rte_graph_t size;
+
+	struct graph **graphs;
+};
+
+/* Capture same node ID across cluster  */
+struct cluster_node {
+	struct rte_graph_cluster_node_stats stat;
+	rte_node_t nb_nodes;
+
+	struct rte_node *nodes[];
+};
+
+struct rte_graph_cluster_stats {
+	/* Header */
+	rte_graph_cluster_stats_cb_t fn;
+	uint32_t cluster_node_size; /* Size of struct cluster_node */
+	rte_node_t max_nodes;
+	int socket_id;
+	void *cookie;
+	size_t sz;
+
+	struct cluster_node clusters[];
+} __rte_cache_aligned;
+
+#define boarder()                                                              \
+	fprintf(f, "+-------------------------------+---------------+--------" \
+		   "-------+---------------+---------------+---------------+-" \
+		   "----------+\n")
+
+static inline void
+print_banner(FILE *f)
+{
+	boarder();
+	fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
+		"|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
+		"|cycles/call|");
+	boarder();
+}
+
+static inline void
+print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
+{
+	double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
+	const uint64_t prev_calls = stat->prev_calls;
+	const uint64_t prev_objs = stat->prev_objs;
+	const uint64_t cycles = stat->cycles;
+	const uint64_t calls = stat->calls;
+	const uint64_t objs = stat->objs;
+	uint64_t call_delta;
+
+	call_delta = calls - prev_calls;
+	objs_per_call =
+		call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
+	cycles_per_call =
+		call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
+			   : 0;
+	ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
+	objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
+	objs_per_sec /= 1000000;
+
+	fprintf(f,
+		"|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
+		"|%-15.3f|%-15.6f|%-11.4f|\n",
+		stat->name, calls, objs, stat->realloc_count, objs_per_call,
+		objs_per_sec, cycles_per_call);
+}
+
+static int
+graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
+		       const struct rte_graph_cluster_node_stats *stat)
+{
+	FILE *f = cookie;
+
+	if (unlikely(is_first))
+		print_banner(f);
+	if (stat->objs)
+		print_node(f, stat);
+	if (unlikely(is_last))
+		boarder();
+
+	return 0;
+};
+
+static struct rte_graph_cluster_stats *
+stats_mem_init(struct cluster *cluster,
+	       const struct rte_graph_cluster_stats_param *prm)
+{
+	size_t sz = sizeof(struct rte_graph_cluster_stats);
+	struct rte_graph_cluster_stats *stats;
+	rte_graph_cluster_stats_cb_t fn;
+	int socket_id = prm->socket_id;
+	uint32_t cluster_node_size;
+
+	/* Fix up callback */
+	fn = prm->fn;
+	if (fn == NULL)
+		fn = graph_cluster_stats_cb;
+
+	cluster_node_size = sizeof(struct cluster_node);
+	/* For a given cluster, max nodes will be the max number of graphs */
+	cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
+	cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
+
+	stats = realloc(NULL, sz);
+	memset(stats, 0, sz);
+	if (stats) {
+		stats->fn = fn;
+		stats->cluster_node_size = cluster_node_size;
+		stats->max_nodes = 0;
+		stats->socket_id = socket_id;
+		stats->cookie = prm->cookie;
+		stats->sz = sz;
+	}
+
+	return stats;
+}
+
+static int
+stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
+		   struct rte_graph *graph, struct graph_node *graph_node)
+{
+	struct rte_graph_cluster_stats *stats = *stats_in;
+	rte_node_t id = graph_node->node->id;
+	struct cluster_node *cluster;
+	struct rte_node *node;
+	rte_node_t count;
+
+	cluster = stats->clusters;
+
+	/* Iterate over cluster node array to find node ID match */
+	for (count = 0; count < stats->max_nodes; count++) {
+		/* Found an existing node in the reel */
+		if (cluster->stat.id == id) {
+			node = graph_node_id_to_ptr(graph, id);
+			if (node == NULL)
+				SET_ERR_JMP(
+					ENOENT, err,
+					"Failed to find node %s in graph %s",
+					graph_node->node->name, graph->name);
+
+			cluster->nodes[cluster->nb_nodes++] = node;
+			return 0;
+		}
+		cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
+	}
+
+	/* Hey, it is a new node, allocate space for it in the reel */
+	stats = realloc(stats, stats->sz + stats->cluster_node_size);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, err, "Realloc failed");
+
+	/* Clear the new struct cluster_node area */
+	cluster = RTE_PTR_ADD(stats, stats->sz),
+	memset(cluster, 0, stats->cluster_node_size);
+	memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
+	cluster->stat.id = graph_node->node->id;
+	cluster->stat.hz = rte_get_timer_hz();
+	node = graph_node_id_to_ptr(graph, id);
+	if (node == NULL)
+		SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s",
+			    graph_node->node->name, graph->name);
+	cluster->nodes[cluster->nb_nodes++] = node;
+
+	stats->sz += stats->cluster_node_size;
+	stats->max_nodes++;
+	*stats_in = stats;
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+stats_mem_fini(struct rte_graph_cluster_stats *stats)
+{
+	free(stats);
+}
+
+static void
+cluster_init(struct cluster *cluster)
+{
+	memset(cluster, 0, sizeof(*cluster));
+}
+
+static int
+cluster_add(struct cluster *cluster, struct graph *graph)
+{
+	rte_graph_t count;
+	size_t sz;
+
+	/* Skip the if graph is already added to cluster */
+	for (count = 0; count < cluster->nb_graphs; count++)
+		if (cluster->graphs[count] == graph)
+			return 0;
+
+	/* Expand the cluster if required to store graph objects */
+	if (cluster->nb_graphs + 1 > cluster->size) {
+		cluster->size = RTE_MAX(1, cluster->size * 2);
+		sz = sizeof(struct graph *) * cluster->size;
+		cluster->graphs = realloc(cluster->graphs, sz);
+		if (cluster->graphs == NULL)
+			SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
+	}
+
+	/* Add graph to cluster */
+	cluster->graphs[cluster->nb_graphs++] = graph;
+	return 0;
+
+free:
+	return -rte_errno;
+}
+
+static void
+cluster_fini(struct cluster *cluster)
+{
+	if (cluster->graphs)
+		free(cluster->graphs);
+}
+
+static int
+expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
+{
+	struct graph_head *graph_head = graph_list_head_get();
+	struct graph *graph;
+	bool found = false;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(graph, graph_head, next) {
+		if (fnmatch(pattern, graph->name, 0) == 0) {
+			if (cluster_add(cluster, graph))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
+			    pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+struct rte_graph_cluster_stats *
+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
+{
+	struct rte_graph_cluster_stats *stats, *rc = NULL;
+	struct graph_node *graph_node;
+	struct cluster cluster;
+	struct graph *graph;
+	const char *pattern;
+	rte_graph_t i;
+
+	/* Sanity checks */
+	if (!rte_graph_has_stats_feature())
+		SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
+
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Invalid param");
+
+	if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
+		SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
+
+	cluster_init(&cluster);
+
+	graph_spinlock_lock();
+	/* Expand graph pattern and add the graph to the cluster */
+	for (i = 0; i < prm->nb_graph_patterns; i++) {
+		pattern = prm->graph_patterns[i];
+		if (expand_pattern_to_cluster(&cluster, pattern))
+			goto bad_pattern;
+	}
+
+	/* Alloc the stats memory */
+	stats = stats_mem_init(&cluster, prm);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
+
+	/* Iterate over M(Graph) x N (Nodes in graph) */
+	for (i = 0; i < cluster.nb_graphs; i++) {
+		graph = cluster.graphs[i];
+		STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+			struct rte_graph *graph_fp = graph->graph;
+			if (stats_mem_populate(&stats, graph_fp, graph_node))
+				goto realloc_fail;
+		}
+	}
+
+	/* Finally copy to hugepage memory to avoid pressure on rte_realloc */
+	rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
+	if (rc)
+		rte_memcpy(rc, stats, stats->sz);
+	else
+		SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
+
+realloc_fail:
+	stats_mem_fini(stats);
+bad_pattern:
+	graph_spinlock_unlock();
+	cluster_fini(&cluster);
+fail:
+	return rc;
+}
+
+void
+rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
+{
+	return rte_free(stat);
+}
+
+static inline void
+cluster_node_arregate_stats(struct cluster_node *cluster)
+{
+	uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+	struct rte_node *node;
+	rte_node_t count;
+
+	for (count = 0; count < cluster->nb_nodes; count++) {
+		node = cluster->nodes[count];
+
+		calls += node->total_calls;
+		objs += node->total_objs;
+		cycles += node->total_cycles;
+		realloc_count += node->realloc_count;
+	}
+
+	stat->calls = calls;
+	stat->objs = objs;
+	stat->cycles = cycles;
+	stat->ts = rte_get_timer_cycles();
+	stat->realloc_count = realloc_count;
+}
+
+static inline void
+cluster_node_store_prev_stats(struct cluster_node *cluster)
+{
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+
+	stat->prev_ts = stat->ts;
+	stat->prev_calls = stat->calls;
+	stat->prev_objs = stat->objs;
+	stat->prev_cycles = stat->cycles;
+}
+
+void
+rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+	int rc = 0;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		cluster_node_arregate_stats(cluster);
+		if (!skip_cb)
+			rc = stat->fn(!count, (count == stat->max_nodes - 1),
+				      stat->cookie, &cluster->stat);
+		cluster_node_store_prev_stats(cluster);
+		if (rc)
+			break;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
+
+void
+rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		struct rte_graph_cluster_node_stats *node = &cluster->stat;
+
+		node->ts = 0;
+		node->calls = 0;
+		node->objs = 0;
+		node->cycles = 0;
+		node->prev_ts = 0;
+		node->prev_calls = 0;
+		node->prev_objs = 0;
+		node->prev_cycles = 0;
+		node->realloc_count = 0;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index fb203a5e2..929a17f84 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_stats.c', 'graph_populate.c')
 headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 851f4772e..adf55d406 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -17,6 +17,11 @@ EXPERIMENTAL {
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
 
+	rte_graph_cluster_stats_create;
+	rte_graph_cluster_stats_destroy;
+	rte_graph_cluster_stats_get;
+	rte_graph_cluster_stats_reset;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 12/28] graph: implement fastpath API routines
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (10 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 11/28] graph: implement stats support jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 13/28] graph: add unit test case jerinj
                     ` (17 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for rte_graph_walk() API. This will perform a walk
on the circular buffer and call the process function of each node
and collect the stats if stats collection is enabled.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md              |   1 +
 lib/librte_graph/graph.c               |  16 +
 lib/librte_graph/rte_graph_version.map |  10 +
 lib/librte_graph/rte_graph_worker.h    | 434 +++++++++++++++++++++++++
 4 files changed, 461 insertions(+)

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5cc50f750..fd2ff64d7 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -160,6 +160,7 @@ The public API headers are grouped by topics:
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
+    [graph_worker]     (@ref rte_graph_worker.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e96363777..d5d816c71 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -474,6 +474,22 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+void __rte_noinline
+__rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,
+			     uint16_t req_size)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
+
 static int
 graph_to_dot(FILE *f, struct graph *graph)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index adf55d406..13b838752 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 	__rte_node_stream_alloc;
+	__rte_node_stream_alloc_size;
 
 	rte_graph_create;
 	rte_graph_destroy;
@@ -16,6 +17,7 @@ EXPERIMENTAL {
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
+	rte_graph_walk;
 
 	rte_graph_cluster_stats_create;
 	rte_graph_cluster_stats_destroy;
@@ -28,10 +30,18 @@ EXPERIMENTAL {
 	rte_node_edge_get;
 	rte_node_edge_shrink;
 	rte_node_edge_update;
+	rte_node_enqueue;
+	rte_node_enqueue_x1;
+	rte_node_enqueue_x2;
+	rte_node_enqueue_x4;
+	rte_node_enqueue_next;
 	rte_node_from_name;
 	rte_node_id_to_name;
 	rte_node_list_dump;
 	rte_node_max_count;
+	rte_node_next_stream_get;
+	rte_node_next_stream_put;
+	rte_node_next_stream_move;
 
 	local: *;
 };
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
index a8133739d..a1bfc498b 100644
--- a/lib/librte_graph/rte_graph_worker.h
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -101,6 +101,440 @@ struct rte_node {
 __rte_experimental
 void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
 
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream with requested number of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param req_size
+ *   Number of objects to be allocated.
+ */
+__rte_experimental
+void __rte_node_stream_alloc_size(struct rte_graph *graph,
+				  struct rte_node *node, uint16_t req_size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Perform graph walk on the circular buffer and invoke the process function
+ * of the nodes and collect the stats.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup function.
+ *
+ * @see rte_graph_lookup()
+ */
+__rte_experimental
+static inline void
+rte_graph_walk(struct rte_graph *graph)
+{
+	const rte_graph_off_t *cir_start = graph->cir_start;
+	const rte_node_t mask = graph->cir_mask;
+	uint32_t head = graph->head;
+	struct rte_node *node;
+	uint64_t start;
+	uint16_t rc;
+	void **objs;
+
+	/*
+	 * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
+	 * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
+	 * in a circular buffer fashion.
+	 *
+	 *	+-----+ <= cir_start - head [number of source nodes]
+	 *	|     |
+	 *	| ... | <= source nodes
+	 *	|     |
+	 *	+-----+ <= cir_start [head = 0] [tail = 0]
+	 *	|     |
+	 *	| ... | <= pending streams
+	 *	|     |
+	 *	+-----+ <= cir_start + mask
+	 */
+	while (likely(head != graph->tail)) {
+		node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
+		RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+		objs = node->objs;
+		rte_prefetch0(objs);
+
+		if (rte_graph_has_stats_feature()) {
+			start = rte_rdtsc();
+			rc = node->process(graph, node, objs, node->idx);
+			node->total_cycles += rte_rdtsc() - start;
+			node->total_calls++;
+			node->total_objs += rc;
+		} else {
+			node->process(graph, node, objs, node->idx);
+		}
+		node->idx = 0;
+		head = likely((int32_t)head > 0) ? head & mask : head;
+	}
+	graph->tail = 0;
+}
+
+/* Fast path helper functions */
+
+/**
+ * @internal
+ *
+ * Enqueue a given node to the tail of the graph reel.
+ *
+ * @param graph
+ *   Pointer Graph object.
+ * @param node
+ *   Pointer to node object to be enqueued.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_tail_update(struct rte_graph *graph, struct rte_node *node)
+{
+	uint32_t tail;
+
+	tail = graph->tail;
+	graph->cir_start[tail++] = node->off;
+	graph->tail = tail & graph->cir_mask;
+}
+
+/**
+ * @internal
+ *
+ * Enqueue sequence prologue function.
+ *
+ * Updates the node to tail of graph reel and resizes the number of objects
+ * available in the stream as needed.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param idx
+ *   Index at which the object enqueue starts from.
+ * @param space
+ *   Space required for the object enqueue.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_prologue(struct rte_graph *graph, struct rte_node *node,
+			    const uint16_t idx, const uint16_t space)
+{
+
+	/* Add to the pending stream list if the node is new */
+	if (idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	if (unlikely(node->size < (idx + space)))
+		__rte_node_stream_alloc(graph, node);
+}
+
+/**
+ * @internal
+ *
+ * Get the node pointer from current node edge id.
+ *
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Edge id of the required node.
+ *
+ * @return
+ *   Pointer to the node denoted by the edge id.
+ */
+static __rte_always_inline struct rte_node *
+__rte_node_next_node_get(struct rte_node *node, rte_edge_t next)
+{
+	RTE_ASSERT(next < node->nb_edges);
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+	node = node->nodes[next];
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+
+	return node;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue the objs to next node for further processing and set
+ * the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param objs
+ *   Objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
+		 rte_edge_t next, void **objs, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, nb_objs);
+
+	rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
+	node->idx = idx + nb_objs;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only one obj to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x1(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 1);
+
+	node->objs[idx++] = obj;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only two objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue two objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   Obj to enqueue.
+ * @param obj1
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x2(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 2);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only four objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue four objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   1st obj to enqueue.
+ * @param obj1
+ *   2nd obj to enqueue.
+ * @param obj2
+ *   3rd obj to enqueue.
+ * @param obj3
+ *   4th obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x4(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1, void *obj2,
+		    void *obj3)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 4);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->objs[idx++] = obj2;
+	node->objs[idx++] = obj3;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue objs to multiple next nodes for further processing and
+ * set the next nodes to pending state in the circular buffer.
+ * objs[i] will be enqueued to nexts[i].
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param nexts
+ *   List of relative next node indices to enqueue objs.
+ * @param objs
+ *   List of objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
+		      rte_edge_t *nexts, void **objs, uint16_t nb_objs)
+{
+	uint16_t i;
+
+	for (i = 0; i < nb_objs; i++)
+		rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the stream of next node to enqueue the objs.
+ * Once done with the updating the objs, needs to call
+ * rte_node_next_stream_put to put the next node to pending state.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to get stream.
+ * @param nb_objs
+ *   Requested free size of the next stream.
+ *
+ * @return
+ *   Valid next stream on success.
+ *
+ * @see rte_node_next_stream_put().
+ */
+__rte_experimental
+static inline void **
+rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+	uint16_t free_space = node->size - idx;
+
+	if (unlikely(free_space < nb_objs))
+		__rte_node_stream_alloc_size(graph, node, nb_objs);
+
+	return &node->objs[idx];
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Put the next stream to pending state in the circular buffer
+ * for further processing. Should be invoked followed by
+ * rte_node_next_stream_get().
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index..
+ * @param idx
+ *   Number of objs updated in the stream after getting the stream using
+ *   rte_node_next_stream_get.
+ *
+ * @see rte_node_next_stream_get().
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t idx)
+{
+	if (unlikely(!idx))
+		return;
+
+	node = __rte_node_next_node_get(node, next);
+	if (node->idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	node->idx += idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Home run scenario, Enqueue all the objs of current node to next
+ * node in optimized way by swapping the streams of both nodes.
+ * Performs good when next node is already not in pending state.
+ * If next node is already in pending state then normal enqueue
+ * will be used.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param src
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index.
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_move(struct rte_graph *graph, struct rte_node *src,
+			  rte_edge_t next)
+{
+	struct rte_node *dst = __rte_node_next_node_get(src, next);
+
+	/* Let swap the pointers if dst don't have valid objs */
+	if (likely(dst->idx == 0)) {
+		void **dobjs = dst->objs;
+		uint16_t dsz = dst->size;
+		dst->objs = src->objs;
+		dst->size = src->size;
+		src->objs = dobjs;
+		src->size = dsz;
+		dst->idx = src->idx;
+		__rte_node_enqueue_tail_update(graph, dst);
+	} else { /* Move the objects from src node to dst node */
+		rte_node_enqueue(graph, src, next, src->objs, src->idx);
+	}
+}
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 13/28] graph: add unit test case
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (11 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 12/28] graph: implement fastpath API routines jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 14/28] graph: add performance testcase jerinj
                     ` (16 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram

From: Kiran Kumar K <kirankumark@marvell.com>

Adding the unit test to test the functionality of node and graph APIs.
Testing includes registering a node, cloning a node, creating a graph,
perform graph walk, collecting stats and all node and graph debug APIs.

example command to test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 app/test/Makefile     |   6 +
 app/test/meson.build  |  11 +-
 app/test/test_graph.c | 819 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 834 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_graph.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 1f080d162..ce2e08e12 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -221,6 +221,10 @@ SRCS-y += test_event_timer_adapter.c
 SRCS-y += test_event_crypto_adapter.c
 endif
 
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
+SRCS-y += test_graph.c
+endif
+
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
 SRCS-y += test_rawdev.c
 endif
@@ -240,6 +244,8 @@ endif
 CFLAGS += -DALLOW_EXPERIMENTAL_API
 
 CFLAGS += -O3
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
 CFLAGS += $(WERROR_FLAGS)
 
 LDLIBS += -lm
diff --git a/app/test/meson.build b/app/test/meson.build
index 351d29cb6..0de16290e 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -51,6 +51,7 @@ test_sources = files('commands.c',
 	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
+	'test_graph.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
@@ -151,7 +152,9 @@ test_deps = ['acl',
 	'rib',
 	'ring',
 	'stack',
-	'timer'
+	'timer',
+	'graph',
+	'node'
 ]
 
 # Each test is marked with flag true/false
@@ -362,6 +365,8 @@ endif
 
 # specify -D_GNU_SOURCE unconditionally
 cflags += '-D_GNU_SOURCE'
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
 
 test_dep_objs = []
 if dpdk_conf.has('RTE_LIBRTE_COMPRESSDEV')
@@ -385,13 +390,15 @@ endforeach
 test_dep_objs += cc.find_library('execinfo', required: false)
 
 link_libs = []
+link_nodes = []
 if get_option('default_library') == 'static'
 	link_libs = dpdk_drivers
+	link_nodes = dpdk_graph_nodes
 endif
 
 dpdk_test = executable('dpdk-test',
 	test_sources,
-	link_whole: link_libs,
+	link_whole: link_libs + link_nodes,
 	dependencies: test_dep_objs,
 	c_args: [cflags, '-DALLOW_EXPERIMENTAL_API'],
 	install_rpath: driver_install_path,
diff --git a/app/test/test_graph.c b/app/test/test_graph.c
new file mode 100644
index 000000000..a90dc8f30
--- /dev/null
+++ b/app/test/test_graph.c
@@ -0,0 +1,819 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static uint16_t test_node_worker_source(struct rte_graph *graph,
+					struct rte_node *node, void **objs,
+					uint16_t nb_objs);
+
+static uint16_t test_node0_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node1_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node2_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node3_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+#define MBUFF_SIZE 512
+#define MAX_NODES  4
+
+static struct rte_mbuf mbuf[MAX_NODES + 1][MBUFF_SIZE];
+static void *mbuf_p[MAX_NODES + 1][MBUFF_SIZE];
+static rte_graph_t graph_id;
+static uint64_t obj_stats[MAX_NODES + 1];
+static uint64_t fn_calls[MAX_NODES + 1];
+
+const char *node_patterns[] = {
+	"test_node_source1",	   "test_node00",
+	"test_node00-test_node11", "test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+const char *node_names[] = {
+	"test_node00",
+	"test_node00-test_node11",
+	"test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+struct test_node_register {
+	char name[RTE_NODE_NAMESIZE];
+	rte_node_process_t process;
+	uint16_t nb_edges;
+	const char *next_nodes[MAX_NODES];
+};
+
+typedef struct {
+	uint32_t idx;
+	struct test_node_register node;
+} test_node_t;
+
+typedef struct {
+	test_node_t test_node[MAX_NODES];
+} test_main_t;
+
+static test_main_t test_main = {
+	.test_node = {
+		{
+			.node = {
+					.name = "test_node00",
+					.process = test_node0_worker,
+					.nb_edges = 2,
+					.next_nodes = {"test_node00-"
+						       "test_node11",
+						       "test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node11",
+					.process = test_node1_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node22",
+					.process = test_node2_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node33"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node33",
+					.process = test_node3_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00"},
+				},
+		},
+	},
+};
+
+static int
+node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	*(uint32_t *)node->ctx = node->id;
+
+	return 0;
+}
+
+static struct rte_node_register test_node_source = {
+	.name = "test_node_source1",
+	.process = test_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.nb_edges = 2,
+	.init = node_init,
+	.next_nodes = {"test_node00", "test_node00-test_node11"},
+};
+RTE_NODE_REGISTER(test_node_source);
+
+static struct rte_node_register test_node0 = {
+	.name = "test_node00",
+	.process = test_node0_worker,
+	.init = node_init,
+};
+RTE_NODE_REGISTER(test_node0);
+
+uint16_t
+test_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	uint32_t obj_node0 = rand() % 100, obj_node1;
+	test_main_t *tm = &test_main;
+	struct rte_mbuf *data;
+	void **next_stream;
+	rte_node_t next;
+	uint32_t i;
+
+	RTE_SET_USED(objs);
+	nb_objs = RTE_GRAPH_BURST_SIZE;
+
+	/* Prepare stream for next node 0 */
+	obj_node0 = nb_objs * obj_node0 * 0.01;
+	next = 0;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node0);
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[0][i];
+		data->udata64 = ((uint64_t)tm->test_node[0].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][i];
+	}
+	rte_node_next_stream_put(graph, node, next, obj_node0);
+
+	/* Prepare stream for next node 1 */
+	obj_node1 = nb_objs - obj_node0;
+	next = 1;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node1);
+	for (i = 0; i < obj_node1; i++) {
+		data = &mbuf[0][obj_node0 + i];
+		data->udata64 = ((uint64_t)tm->test_node[1].idx << 32) | i;
+		if ((i + 1) == obj_node1)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][obj_node0 + i];
+	}
+
+	rte_node_next_stream_put(graph, node, next, obj_node1);
+	obj_stats[0] += nb_objs;
+	fn_calls[0] += 1;
+	return nb_objs;
+}
+
+uint16_t
+test_node0_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+
+	if (*(uint32_t *)node->ctx == test_node0.id) {
+		uint32_t obj_node0 = rand() % 100, obj_node1;
+		struct rte_mbuf *data;
+		uint8_t second_pass = 0;
+		uint32_t count = 0;
+		uint32_t i;
+
+		obj_stats[1] += nb_objs;
+		fn_calls[1] += 1;
+
+		for (i = 0; i < nb_objs; i++) {
+			data = (struct rte_mbuf *)objs[i];
+			if ((data->udata64 >> 32) != tm->test_node[0].idx) {
+				printf("Data idx miss match at node 0, expected"
+				       " = %u got = %u\n",
+				       tm->test_node[0].idx,
+				       (uint32_t)(data->udata64 >> 32));
+				goto end;
+			}
+
+			if ((data->udata64 & 0xffff) != (i - count)) {
+				printf("Expected buff count miss match at "
+				       "node 0\n");
+				goto end;
+			}
+
+			if (data->udata64 & (0x1 << 16))
+				count = i + 1;
+			if (data->udata64 & (0x1 << 17))
+				second_pass = 1;
+		}
+
+		if (count != i) {
+			printf("Count mismatch at node 0\n");
+			goto end;
+		}
+
+		obj_node0 = nb_objs * obj_node0 * 0.01;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[1][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[1].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[1][0],
+				 obj_node0);
+
+		obj_node1 = nb_objs - obj_node0;
+		for (i = 0; i < obj_node1; i++) {
+			data = &mbuf[1][obj_node0 + i];
+			data->udata64 =
+				((uint64_t)tm->test_node[2].idx << 32) | i;
+			if ((i + 1) == obj_node1)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 1, (void **)&mbuf_p[1][obj_node0],
+				 obj_node1);
+
+	} else if (*(uint32_t *)node->ctx == tm->test_node[1].idx) {
+		test_node1_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[2].idx) {
+		test_node2_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[3].idx) {
+		test_node3_worker(graph, node, objs, nb_objs);
+	} else {
+		printf("Unexpected node context\n");
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node1_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	uint32_t obj_node0 = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t i;
+
+	obj_stats[2] += nb_objs;
+	fn_calls[2] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[1].idx) {
+			printf("Data idx miss match at node 1, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[1].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 1\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 1\n");
+		goto end;
+	}
+
+	obj_node0 = nb_objs;
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[2][i];
+		data->udata64 = ((uint64_t)tm->test_node[2].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		if (second_pass)
+			data->udata64 |= (1 << 17);
+	}
+	rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[2][0], obj_node0);
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node2_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[3] += nb_objs;
+	fn_calls[3] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[2].idx) {
+			printf("Data idx miss match at node 2, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[2].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 2\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 2\n");
+		goto end;
+	}
+
+	if (!second_pass) {
+		obj_node0 = nb_objs;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[3][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[3].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[3][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node3_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[4] += nb_objs;
+	fn_calls[4] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[3].idx) {
+			printf("Data idx miss match at node 3, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[3].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 3\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 3\n");
+		goto end;
+	}
+
+	if (second_pass) {
+		printf("Unexpected buffers are at node 3\n");
+		goto end;
+	} else {
+		obj_node0 = nb_objs * 2;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[4][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[0].idx << 32) | i;
+			data->udata64 |= (1 << 17);
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[4][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+static int
+test_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	int i;
+
+	/* Verify the name with ID */
+	for (i = 1; i < MAX_NODES; i++) {
+		char *name = rte_node_id_to_name(tm->test_node[i].idx);
+		if (strcmp(name, node_names[i]) != 0) {
+			printf("Test node name verify by ID = %d failed "
+			       "Expected = %s, got %s\n",
+			       i, node_names[i], name);
+			return -1;
+		}
+	}
+
+	/* Verify by name */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t idx = rte_node_from_name(node_names[i]);
+		if (idx != tm->test_node[i].idx) {
+			printf("Test node ID verify by name = %s failed "
+			       "Expected = %d, got %d\n",
+			       node_names[i], tm->test_node[i].idx, idx);
+			return -1;
+		}
+	}
+
+	/* Verify edge count */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t count = rte_node_edge_count(tm->test_node[i].idx);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+	}
+
+	/* Verify edge names */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t j, count;
+		char **next_edges;
+
+		count = rte_node_edge_get(tm->test_node[i].idx, NULL);
+		if (count != tm->test_node[i].node.nb_edges * sizeof(char *)) {
+			printf("Test number of edge count for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+		next_edges = malloc(count);
+		count = rte_node_edge_get(tm->test_node[i].idx, next_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		for (j = 0; j < count; j++) {
+			if (strcmp(next_edges[j],
+				   tm->test_node[i].node.next_nodes[j]) != 0) {
+				printf("Edge name miss match, expected = %s got = %s\n",
+				       tm->test_node[i].node.next_nodes[j],
+				       next_edges[j]);
+				return -1;
+			}
+		}
+		free(next_edges);
+	}
+
+	return 0;
+}
+
+static int
+test_node_clone(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id, dummy_id;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	tm->test_node[0].idx = node_id;
+
+	/* Clone with same name, should fail */
+	dummy_id = rte_node_clone(node_id, "test_node00");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid id when clone with same name, Expecting fail\n");
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		tm->test_node[i].idx =
+			rte_node_clone(node_id, tm->test_node[i].node.name);
+		if (rte_node_is_invalid(tm->test_node[i].idx)) {
+			printf("Got invalid node id\n");
+			return -1;
+		}
+	}
+
+	/* Clone from cloned node should fail */
+	dummy_id = rte_node_clone(tm->test_node[1].idx, "dummy_node");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid node id when cloning from cloned node, expected fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_update_edges(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id;
+	uint16_t count;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	count = rte_node_edge_update(node_id, 0,
+				     tm->test_node[0].node.next_nodes,
+				     tm->test_node[0].node.nb_edges);
+	if (count != tm->test_node[0].node.nb_edges) {
+		printf("Update edges failed expected: %d got = %d\n",
+		       tm->test_node[0].node.nb_edges, count);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		count = rte_node_edge_update(tm->test_node[i].idx, 0,
+					     tm->test_node[i].node.next_nodes,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Update edges failed expected: %d got = %d\n",
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		count = rte_node_edge_shrink(tm->test_node[i].idx,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Shrink edges failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_create_graph(void)
+{
+	static const char *node_patterns_dummy[] = {
+		"test_node_source1",	   "test_node00",
+		"test_node00-test_node11", "test_node00-test_node22",
+		"test_node00-test_node33", "test_node00-dummy_node",
+	};
+	struct rte_graph_param gconf = {
+		.socket_id = SOCKET_ID_ANY,
+		.nb_node_patterns = 6,
+		.node_patterns = node_patterns_dummy,
+	};
+	uint32_t dummy_node_id;
+	uint32_t node_id;
+
+	node_id = rte_node_from_name("test_node00");
+	dummy_node_id = rte_node_clone(node_id, "dummy_node");
+	if (rte_node_is_invalid(dummy_node_id)) {
+		printf("Got invalid node id\n");
+		return -1;
+	}
+
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id != RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation success with isolated node, expected graph creation fail\n");
+		return -1;
+	}
+
+	gconf.nb_node_patterns = 5;
+	gconf.node_patterns = node_patterns;
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+test_graph_walk(void)
+{
+	struct rte_graph *graph = rte_graph_lookup("worker0");
+	int i;
+
+	if (!graph) {
+		printf("Graph lookup failed\n");
+		return -1;
+	}
+
+	for (i = 0; i < 5; i++)
+		rte_graph_walk(graph);
+	return 0;
+}
+
+static int
+test_graph_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	struct rte_node *node;
+	int i;
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get(graph_id, tm->test_node[i].idx);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get_by_name("worker0", node_names[i]);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+graph_cluster_stats_cb_t(bool is_first, bool is_last, void *cookie,
+			 const struct rte_graph_cluster_node_stats *st)
+{
+	int i;
+
+	RTE_SET_USED(is_first);
+	RTE_SET_USED(is_last);
+	RTE_SET_USED(cookie);
+
+	for (i = 0; i < MAX_NODES + 1; i++) {
+		rte_node_t id = rte_node_from_name(node_patterns[i]);
+		if (id == st->id) {
+			if (obj_stats[i] != st->objs) {
+				printf("Obj count miss match for node = %s expected = %"PRId64", got=%"PRId64"\n",
+				       node_patterns[i], obj_stats[i],
+				       st->objs);
+				return -1;
+			}
+
+			if (fn_calls[i] != st->calls) {
+				printf("Func call miss match for node = %s expected = %"PRId64", got = %"PRId64"\n",
+				       node_patterns[i], fn_calls[i],
+				       st->calls);
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+test_print_stats(void)
+{
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker0";
+
+	if (!rte_graph_has_stats_feature())
+		return 0;
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+	s_param.fn = graph_cluster_stats_cb_t;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL) {
+		printf("Unable to get stats\n");
+		return -1;
+	}
+	/* Clear screen and move to top left */
+	rte_graph_cluster_stats_get(stats, 0);
+	rte_graph_cluster_stats_destroy(stats);
+
+	return 0;
+}
+
+static int
+graph_setup(void)
+{
+	int i, j;
+
+	for (i = 0; i <= MAX_NODES; i++) {
+		for (j = 0; j < MBUFF_SIZE; j++)
+			mbuf_p[i][j] = &mbuf[i][j];
+	}
+	if (test_node_clone()) {
+		printf("test_node_clone: fail\n");
+		return -1;
+	}
+	printf("test_node_clone: pass\n");
+
+	return 0;
+}
+
+static void
+graph_teardown(void)
+{
+	rte_graph_t id;
+
+	id = rte_graph_destroy("worker0");
+	if (id == RTE_GRAPH_ID_INVALID)
+		printf("Graph Destroy failed\n");
+}
+
+static struct unit_test_suite graph_testsuite = {
+	.suite_name = "Graph library test suite",
+	.setup = graph_setup,
+	.teardown = graph_teardown,
+	.unit_test_cases = {
+		TEST_CASE(test_update_edges),
+		TEST_CASE(test_lookup_functions),
+		TEST_CASE(test_create_graph),
+		TEST_CASE(test_graph_lookup_functions),
+		TEST_CASE(test_graph_walk),
+		TEST_CASE(test_print_stats),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+graph_autotest_fn(void)
+{
+	return unit_test_suite_runner(&graph_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_autotest, graph_autotest_fn);
+
+static int
+test_node_list_dump(void)
+{
+	rte_node_list_dump(stdout);
+
+	return TEST_SUCCESS;
+}
+REGISTER_TEST_COMMAND(node_list_dump, test_node_list_dump);
+
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 14/28] graph: add performance testcase
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (12 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 13/28] graph: add unit test case jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 15/28] node: add log infra and null node jerinj
                     ` (15 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add unit test framework to create and test performance of various graph
models.

example command to test:

echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 app/test/Makefile          |    1 +
 app/test/meson.build       |    1 +
 app/test/test_graph_perf.c | 1057 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1059 insertions(+)
 create mode 100644 app/test/test_graph_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index ce2e08e12..77276f300 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -223,6 +223,7 @@ endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
 SRCS-y += test_graph.c
+SRCS-y += test_graph_perf.c
 endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
diff --git a/app/test/meson.build b/app/test/meson.build
index 0de16290e..728d20c1f 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -52,6 +52,7 @@ test_sources = files('commands.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_graph.c',
+	'test_graph_perf.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
diff --git a/app/test/test_graph_perf.c b/app/test/test_graph_perf.c
new file mode 100644
index 000000000..a629f1e35
--- /dev/null
+++ b/app/test/test_graph_perf.c
@@ -0,0 +1,1057 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_lcore.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+#define TEST_GRAPH_PERF_MZ	     "graph_perf_data"
+#define TEST_GRAPH_SRC_NAME	     "test_graph_perf_source"
+#define TEST_GRAPH_SRC_BRST_ONE_NAME "test_graph_perf_source_one"
+#define TEST_GRAPH_WRK_NAME	     "test_graph_perf_worker"
+#define TEST_GRAPH_SNK_NAME	     "test_graph_perf_sink"
+
+#define SOURCES(map)	     RTE_DIM(map)
+#define STAGES(map)	     RTE_DIM(map)
+#define NODES_PER_STAGE(map) RTE_DIM(map[0])
+#define SINKS(map)	     RTE_DIM(map[0])
+
+#define MAX_EDGES_PER_NODE 7
+
+struct test_node_data {
+	uint8_t node_id;
+	uint8_t is_sink;
+	uint8_t next_nodes[MAX_EDGES_PER_NODE];
+	uint8_t next_percentage[MAX_EDGES_PER_NODE];
+};
+
+struct test_graph_perf {
+	uint16_t nb_nodes;
+	rte_graph_t graph_id;
+	struct test_node_data *node_data;
+};
+
+struct graph_lcore_data {
+	uint8_t done;
+	rte_graph_t graph_id;
+};
+
+static struct test_node_data *
+graph_get_node_data(struct test_graph_perf *graph_data, rte_node_t id)
+{
+	struct test_node_data *node_data = NULL;
+	int i;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		if (graph_data->node_data[i].node_id == id) {
+			node_data = &graph_data->node_data[i];
+			break;
+		}
+
+	return node_data;
+}
+
+static int
+test_node_ctx_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	struct test_graph_perf *graph_data;
+	struct test_node_data *node_data;
+	const struct rte_memzone *mz;
+	rte_node_t nid = node->id;
+	rte_edge_t edge = 0;
+	int i;
+
+	RTE_SET_USED(graph);
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+	node_data = graph_get_node_data(graph_data, nid);
+	node->ctx[0] = node->nb_edges;
+	for (i = 0; i < node->nb_edges && !node_data->is_sink; i++, edge++) {
+		node->ctx[i + 1] = edge;
+		node->ctx[i + 9] = node_data->next_percentage[i];
+	}
+
+	return 0;
+}
+
+/* Source node function */
+static uint16_t
+test_perf_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			     void **objs, uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9] * RTE_GRAPH_BURST_SIZE) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return RTE_GRAPH_BURST_SIZE;
+}
+
+static struct rte_node_register test_graph_perf_source = {
+	.name = TEST_GRAPH_SRC_NAME,
+	.process = test_perf_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source);
+
+static uint16_t
+test_perf_node_worker_source_burst_one(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9]) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return 1;
+}
+
+static struct rte_node_register test_graph_perf_source_burst_one = {
+	.name = TEST_GRAPH_SRC_BRST_ONE_NAME,
+	.process = test_perf_node_worker_source_burst_one,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source_burst_one);
+
+/* Worker node function */
+static uint16_t
+test_perf_node_worker(struct rte_graph *graph, struct rte_node *node,
+		      void **objs, uint16_t nb_objs)
+{
+	uint16_t next = 0;
+	uint16_t enq = 0;
+	uint16_t count;
+	int i;
+
+	/* Move stream for single next node */
+	if (node->ctx[0] == 1) {
+		rte_node_next_stream_move(graph, node, node->ctx[1]);
+		return nb_objs;
+	}
+
+	/* Enqueue objects to next nodes proportionally */
+	for (i = 0; i < node->ctx[0]; i++) {
+		next = node->ctx[i + 1];
+		count = (node->ctx[i + 9] * nb_objs) / 100;
+		enq += count;
+		while (count) {
+			switch (count & (4 - 1)) {
+			case 0:
+				rte_node_enqueue_x4(graph, node, next, objs[0],
+						    objs[1], objs[2], objs[3]);
+				objs += 4;
+				count -= 4;
+				break;
+			case 1:
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 1;
+				count -= 1;
+				break;
+			case 2:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				objs += 2;
+				count -= 2;
+				break;
+			case 3:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 3;
+				count -= 3;
+				break;
+			}
+		}
+	}
+
+	if (enq != nb_objs)
+		rte_node_enqueue(graph, node, next, objs, nb_objs - enq);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_worker = {
+	.name = TEST_GRAPH_WRK_NAME,
+	.process = test_perf_node_worker,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_worker);
+
+/* Last node in graph a.k.a sink node */
+static uint16_t
+test_perf_node_sink(struct rte_graph *graph, struct rte_node *node, void **objs,
+		    uint16_t nb_objs)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_sink = {
+	.name = TEST_GRAPH_SNK_NAME,
+	.process = test_perf_node_sink,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_sink);
+
+static int
+graph_perf_setup(void)
+{
+	if (rte_lcore_count() < 2) {
+		printf("Test requires at least 2 lcores\n");
+		return TEST_SKIPPED;
+	}
+
+	return 0;
+}
+
+static void
+graph_perf_teardown(void)
+{
+}
+
+static inline rte_node_t
+graph_node_get(const char *pname, char *nname)
+{
+	rte_node_t pnode_id = rte_node_from_name(pname);
+	char lookup_name[RTE_NODE_NAMESIZE];
+	rte_node_t node_id;
+
+	snprintf(lookup_name, RTE_NODE_NAMESIZE, "%s-%s", pname, nname);
+	node_id = rte_node_from_name(lookup_name);
+
+	if (node_id != RTE_NODE_ID_INVALID) {
+		if (rte_node_edge_count(node_id))
+			rte_node_edge_shrink(node_id, 0);
+		return node_id;
+	}
+
+	return rte_node_clone(pnode_id, nname);
+}
+
+static uint16_t
+graph_node_count_edges(uint32_t stage, uint16_t node, uint16_t nodes_per_stage,
+		       uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+		       char *ename[], struct test_node_data *node_data,
+		       rte_node_t **node_map)
+{
+	uint8_t total_percent = 0;
+	uint16_t edges = 0;
+	int i;
+
+	for (i = 0; i < nodes_per_stage && edges < MAX_EDGES_PER_NODE; i++) {
+		if (edge_map[stage + 1][i][node]) {
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[stage + 1][i]));
+			node_data->next_nodes[edges] = node_map[stage + 1][i];
+			node_data->next_percentage[edges] =
+				edge_map[stage + 1][i][node];
+			edges++;
+			total_percent += edge_map[stage + 1][i][node];
+		}
+	}
+
+	if (edges >= MAX_EDGES_PER_NODE || (edges && total_percent != 100)) {
+		for (i = 0; i < edges; i++)
+			free(ename[i]);
+		return RTE_EDGE_ID_INVALID;
+	}
+
+	return edges;
+}
+
+static int
+graph_init(const char *gname, uint8_t nb_srcs, uint8_t nb_sinks,
+	   uint32_t stages, uint16_t nodes_per_stage,
+	   uint8_t src_map[][nodes_per_stage], uint8_t snk_map[][nb_sinks],
+	   uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+	   uint8_t burst_one)
+{
+	struct test_graph_perf *graph_data;
+	char nname[RTE_NODE_NAMESIZE / 2];
+	struct test_node_data *node_data;
+	char *ename[nodes_per_stage];
+	struct rte_graph_param gconf;
+	const struct rte_memzone *mz;
+	uint8_t total_percent = 0;
+	rte_node_t *src_nodes;
+	rte_node_t *snk_nodes;
+	rte_node_t **node_map;
+	char **node_patterns;
+	rte_graph_t graph_id;
+	rte_edge_t edges;
+	rte_edge_t count;
+	uint32_t i, j, k;
+
+	mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
+				 sizeof(struct test_graph_perf), 0, 0);
+	if (mz == NULL) {
+		printf("Failed to allocate graph common memory\n");
+		return -ENOMEM;
+	}
+
+	graph_data = mz->addr;
+	graph_data->nb_nodes = 0;
+	graph_data->node_data =
+		malloc(sizeof(struct test_node_data) *
+		       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (graph_data->node_data == NULL) {
+		printf("Failed to reserve memzone for graph data\n");
+		goto memzone_free;
+	}
+
+	node_patterns = malloc(sizeof(char *) *
+			       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (node_patterns == NULL) {
+		printf("Failed to reserve memory for node patterns\n");
+		goto data_free;
+	}
+
+	src_nodes = malloc(sizeof(rte_node_t) * nb_srcs);
+	if (src_nodes == NULL) {
+		printf("Failed to reserve memory for src nodes\n");
+		goto pattern_free;
+	}
+
+	snk_nodes = malloc(sizeof(rte_node_t) * nb_sinks);
+	if (snk_nodes == NULL) {
+		printf("Failed to reserve memory for snk nodes\n");
+		goto src_free;
+	}
+
+	node_map = malloc(sizeof(rte_node_t *) * stages +
+			  sizeof(rte_node_t) * nodes_per_stage * stages);
+	if (node_map == NULL) {
+		printf("Failed to reserve memory for node map\n");
+		goto snk_free;
+	}
+
+	/* Setup the Graph */
+	for (i = 0; i < stages; i++) {
+		node_map[i] =
+			(rte_node_t *)(node_map + stages) + nodes_per_stage * i;
+		for (j = 0; j < nodes_per_stage; j++) {
+			total_percent = 0;
+			for (k = 0; k < nodes_per_stage; k++)
+				total_percent += edge_map[i][j][k];
+			if (!total_percent)
+				continue;
+			node_patterns[graph_data->nb_nodes] =
+				malloc(RTE_NODE_NAMESIZE);
+			if (node_patterns[graph_data->nb_nodes] == NULL) {
+				printf("Failed to create memory for pattern\n");
+				goto pattern_name_free;
+			}
+
+			/* Clone a worker node */
+			snprintf(nname, sizeof(nname), "%d-%d", i, j);
+			node_map[i][j] =
+				graph_node_get(TEST_GRAPH_WRK_NAME, nname);
+			if (node_map[i][j] == RTE_NODE_ID_INVALID) {
+				printf("Failed to create node[%s]\n", nname);
+				graph_data->nb_nodes++;
+				goto pattern_name_free;
+			}
+			snprintf(node_patterns[graph_data->nb_nodes],
+				 RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[i][j]));
+			node_data =
+				&graph_data->node_data[graph_data->nb_nodes];
+			node_data->node_id = node_map[i][j];
+			node_data->is_sink = false;
+			graph_data->nb_nodes++;
+		}
+	}
+
+	for (i = 0; i < stages - 1; i++) {
+		for (j = 0; j < nodes_per_stage; j++) {
+			/* Count edges i.e connections of worker node to next */
+			node_data =
+				graph_get_node_data(graph_data, node_map[i][j]);
+			edges = graph_node_count_edges(i, j, nodes_per_stage,
+						       edge_map, ename,
+						       node_data, node_map);
+			if (edges == RTE_EDGE_ID_INVALID) {
+				printf("Invalid edge configuration\n");
+				goto pattern_name_free;
+			}
+			if (!edges)
+				continue;
+
+			/* Connect a node in stage 'i' to nodes
+			 * in stage 'i + 1' with edges.
+			 */
+			count = rte_node_edge_update(
+				node_map[i][j], 0,
+				(const char **)(uintptr_t)ename, edges);
+			for (k = 0; k < edges; k++)
+				free(ename[k]);
+			if (count != edges) {
+				printf("Couldn't add edges %d %d\n", edges,
+				       count);
+				goto pattern_name_free;
+			}
+		}
+	}
+
+	/* Setup Source nodes */
+	for (i = 0; i < nb_srcs; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+		/* Clone a source node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		src_nodes[i] =
+			graph_node_get(burst_one ? TEST_GRAPH_SRC_BRST_ONE_NAME
+						 : TEST_GRAPH_SRC_NAME,
+				       nname);
+		if (src_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(src_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = src_nodes[i];
+		node_data->is_sink = false;
+		graph_data->nb_nodes++;
+
+		/* Prepare next node list  to connect to */
+		for (j = 0; j < nodes_per_stage; j++) {
+			if (!src_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[0][j]));
+			node_data->next_nodes[edges] = node_map[0][j];
+			node_data->next_percentage[edges] = src_map[i][j];
+			edges++;
+			total_percent += src_map[i][j];
+		}
+
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[j]);
+			goto pattern_name_free;
+		}
+
+		/* Connect to list of next nodes using edges */
+		count = rte_node_edge_update(src_nodes[i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Setup Sink nodes */
+	for (i = 0; i < nb_sinks; i++) {
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+
+		/* Clone a sink node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		snk_nodes[i] = graph_node_get(TEST_GRAPH_SNK_NAME, nname);
+		if (snk_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(snk_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = snk_nodes[i];
+		node_data->is_sink = true;
+		graph_data->nb_nodes++;
+	}
+
+	/* Connect last stage worker nodes to sink nodes */
+	for (i = 0; i < nodes_per_stage; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_data = graph_get_node_data(graph_data,
+						node_map[stages - 1][i]);
+		/* Prepare list of sink nodes to connect to */
+		for (j = 0; j < nb_sinks; j++) {
+			if (!snk_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(snk_nodes[j]));
+			node_data->next_nodes[edges] = snk_nodes[j];
+			node_data->next_percentage[edges] = snk_map[i][j];
+			edges++;
+			total_percent += snk_map[i][j];
+		}
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[i]);
+			goto pattern_name_free;
+		}
+
+		/* Connect a worker node to a list of sink nodes */
+		count = rte_node_edge_update(node_map[stages - 1][i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Create a Graph */
+	gconf.socket_id = SOCKET_ID_ANY;
+	gconf.nb_node_patterns = graph_data->nb_nodes;
+	gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
+
+	graph_id = rte_graph_create(gname, &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		goto pattern_name_free;
+	}
+	graph_data->graph_id = graph_id;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+	free(snk_nodes);
+	free(src_nodes);
+	free(node_patterns);
+	return 0;
+
+pattern_name_free:
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+snk_free:
+	free(snk_nodes);
+src_free:
+	free(src_nodes);
+pattern_free:
+	free(node_patterns);
+data_free:
+	free(graph_data->node_data);
+memzone_free:
+	rte_memzone_free(mz);
+	return -ENOMEM;
+}
+
+/* Worker thread function */
+static int
+_graph_perf_wrapper(void *args)
+{
+	struct graph_lcore_data *data = args;
+	struct rte_graph *graph;
+
+	/* Lookup graph */
+	graph = rte_graph_lookup(rte_graph_id_to_name(data->graph_id));
+
+	/* Graph walk until done */
+	while (!data->done)
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
+static int
+measure_perf_get(rte_graph_t graph_id)
+{
+	const char *pattern = rte_graph_id_to_name(graph_id);
+	uint32_t lcore_id = rte_get_next_lcore(-1, 1, 0);
+	struct rte_graph_cluster_stats_param param;
+	struct rte_graph_cluster_stats *stats;
+	struct graph_lcore_data *data;
+
+	data = rte_zmalloc("Graph_perf", sizeof(struct graph_lcore_data),
+			   RTE_CACHE_LINE_SIZE);
+	data->graph_id = graph_id;
+	data->done = 0;
+
+	/* Run graph worker thread function */
+	rte_eal_remote_launch(_graph_perf_wrapper, data, lcore_id);
+
+	/* Collect stats for few msecs */
+	if (rte_graph_has_stats_feature()) {
+		memset(&param, 0, sizeof(param));
+		param.f = stdout;
+		param.socket_id = SOCKET_ID_ANY;
+		param.graph_patterns = &pattern;
+		param.nb_graph_patterns = 1;
+
+		stats = rte_graph_cluster_stats_create(&param);
+		if (stats == NULL) {
+			printf("Failed to create stats\n");
+			return -ENOMEM;
+		}
+
+		rte_delay_ms(3E2);
+		rte_graph_cluster_stats_get(stats, true);
+		rte_delay_ms(1E3);
+		rte_graph_cluster_stats_get(stats, false);
+		rte_graph_cluster_stats_destroy(stats);
+	} else
+		rte_delay_ms(1E3);
+
+	data->done = 1;
+	rte_eal_wait_lcore(lcore_id);
+
+	return 0;
+}
+
+static inline void
+graph_fini(void)
+{
+	const struct rte_memzone *mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	struct test_graph_perf *graph_data;
+
+	if (mz == NULL)
+		return;
+	graph_data = mz->addr;
+
+	rte_graph_destroy(rte_graph_id_to_name(graph_data->graph_id));
+	free(graph_data->node_data);
+	rte_memzone_free(rte_memzone_lookup(TEST_GRAPH_PERF_MZ));
+}
+
+static int
+measure_perf(void)
+{
+	const struct rte_memzone *mz;
+	struct test_graph_perf *graph_data;
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+
+	return measure_perf_get(graph_data->graph_id);
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk_brst_one(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_2src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_2snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_tree_4s_4n_1src_4snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_reverse_tree_3s_4n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_parallel_tree_5s_4n_4src_4snk(void)
+{
+	return measure_perf();
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr_brst_one(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 1);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			2
+ * sink:		1
+ */
+static inline int
+graph_init_hr_multi_src(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = {
+		{100}, {100}
+	};
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		2
+ */
+static inline int
+graph_init_hr_multi_snk(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][2] = { {50, 50} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		4
+ * src:			1
+ * sink:		4
+ */
+static inline int
+graph_init_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 0, 0, 0},
+			{50, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{33, 33, 0, 0},
+			{34, 34, 0, 0},
+			{33, 33, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0}
+		}
+	};
+	uint8_t src_map[][4] = { {100, 0, 0, 0} };
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		3
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_reverse_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25}
+		},
+		{
+			{33, 33, 33, 33},
+			{33, 33, 33, 33},
+			{34, 34, 34, 34},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 50, 50, 0},
+			{50, 50, 50, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+	};
+	uint8_t src_map[][4] = { {25, 25, 25, 25} };
+	uint8_t snk_map[][1] = { {100}, {100}, {0}, {0} };
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		5
+ * src:			4
+ * sink:		4
+ */
+static inline int
+graph_init_parallel_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+	};
+	uint8_t src_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_parallel", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/** Graph Creation cheat sheet
+ *  edge_map -> dictates graph flow from worker stage 0 to worker stage n-1.
+ *  src_map  -> dictates source nodes enqueue percentage to worker stage 0.
+ *  snk_map  -> dictates stage n-1 enqueue percentage to sink.
+ *
+ *  Layout:
+ *  edge_map[<nb_stages>][<nodes_per_stg>][<nodes_in_nxt_stg = nodes_per_stg>]
+ *  src_map[<nb_sources>][<nodes_in_stage0 = nodes_per_stage>]
+ *  snk_map[<nodes_in_stage(n-1) = nodes_per_stage>][<nb_sinks>]
+ *
+ *  The last array dictates the percentage of received objs to enqueue to next
+ *  stage.
+ *
+ *  Note: edge_map[][0][] will always be unused as it will receive from source
+ *
+ *  Example:
+ *	Graph:
+ *	http://bit.ly/2PqbqOy
+ *	Each stage(n) connects to all nodes in the next stage in decreasing
+ *	order.
+ *	Since we can't resize the edge_map dynamically we get away by creating
+ *	dummy nodes and assigning 0 percentages.
+ *	Max nodes across all stages = 4
+ *	stages = 3
+ *	nb_src = 1
+ *	nb_snk = 1
+ *			   // Stages
+ *	edge_map[][4][4] = {
+ *		// Nodes per stage
+ *		{
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25}
+ *		},	// This will be unused.
+ *		{
+ *		    // Nodes enabled in current stage + prev stage enq %
+ *		    {33, 33, 33, 33},
+ *		    {33, 33, 33, 33},
+ *		    {34, 34, 34, 34},
+ *		    {0, 0, 0, 0}
+ *		},
+ *		{
+ *		    {50, 50, 50, 0},
+ *		    {50, 50, 50, 0},
+ *		    {0, 0, 0, 0},
+ *		    {0, 0, 0, 0}
+ *		},
+ *	};
+ *	Above, each stage tells how much it should receive from previous except
+ *	from stage_0.
+ *
+ *	src_map[][4] = { {25, 25, 25, 25} };
+ *	Here, we tell each source the % it has to send to stage_0 nodes. In
+ *	case we want 2 source node we can declare as
+ *	src_map[][4] = { {25, 25, 25, 25}, {25, 25, 25, 25} };
+ *
+ *	snk_map[][1] = { {100}, {100}, {0}, {0} }
+ *	Here, we tell stage - 1 nodes how much to enqueue to sink_0.
+ *	If we have 2 sinks we can do as follows
+ *	snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
+ */
+
+static struct unit_test_suite graph_perf_testsuite = {
+	.suite_name = "Graph library performance test suite",
+	.setup = graph_perf_setup,
+	.teardown = graph_perf_teardown,
+	.unit_test_cases = {
+		TEST_CASE_ST(graph_init_hr, graph_fini,
+			     graph_hr_4s_1n_1src_1snk),
+		TEST_CASE_ST(graph_init_hr_brst_one, graph_fini,
+			     graph_hr_4s_1n_1src_1snk_brst_one),
+		TEST_CASE_ST(graph_init_hr_multi_src, graph_fini,
+			     graph_hr_4s_1n_2src_1snk),
+		TEST_CASE_ST(graph_init_hr_multi_snk, graph_fini,
+			     graph_hr_4s_1n_1src_2snk),
+		TEST_CASE_ST(graph_init_tree, graph_fini,
+			     graph_tree_4s_4n_1src_4snk),
+		TEST_CASE_ST(graph_init_reverse_tree, graph_fini,
+			     graph_reverse_tree_3s_4n_1src_1snk),
+		TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
+			     graph_parallel_tree_5s_4n_4src_4snk),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+test_graph_perf_func(void)
+{
+	return unit_test_suite_runner(&graph_perf_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_perf_autotest, test_graph_perf_func);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 15/28] node: add log infra and null node
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (13 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 14/28] graph: add performance testcase jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 16/28] node: add ethdev Rx node jerinj
                     ` (14 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic,
	Nithin Dabilpuram, Pavan Nikhilesh, Bruce Richardson
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add log infra for node specific logging.
Also, add null rte_node that just ignores all the objects
directed to it.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 MAINTAINERS                          |  5 +++++
 config/common_base                   |  5 +++++
 doc/api/doxy-api.conf.in             |  1 +
 lib/Makefile                         |  3 +++
 lib/librte_node/Makefile             | 22 ++++++++++++++++++++++
 lib/librte_node/log.c                | 14 ++++++++++++++
 lib/librte_node/meson.build          |  8 ++++++++
 lib/librte_node/node_private.h       | 22 ++++++++++++++++++++++
 lib/librte_node/null.c               | 23 +++++++++++++++++++++++
 lib/librte_node/rte_node_version.map |  6 ++++++
 lib/meson.build                      |  5 ++++-
 meson.build                          |  1 +
 mk/rte.app.mk                        |  1 +
 13 files changed, 115 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/rte_node_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index bc7085983..c1acaedab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1474,6 +1474,11 @@ M: Jerin Jacob <jerinj@marvell.com>
 M: Kiran Kumar K <kirankumark@marvell.com>
 F: lib/librte_graph/
 
+Nodes - EXPERIMENTAL
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+M: Pavan Nikhilesh <pbhagavatula@marvell.com>
+F: lib/librte_node/
+
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index 32f982136..1ed5187dc 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1081,6 +1081,11 @@ CONFIG_RTE_LIBRTE_GRAPH=y
 CONFIG_RTE_GRAPH_BURST_SIZE=256
 CONFIG_RTE_LIBRTE_GRAPH_STATS=y
 
+#
+# Compile librte_node
+#
+CONFIG_RTE_LIBRTE_NODE=y
+
 #
 # Compile the test application
 #
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 759a7213e..1d4f1a37d 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -49,6 +49,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_mempool \
                           @TOPDIR@/lib/librte_meter \
                           @TOPDIR@/lib/librte_metrics \
+                          @TOPDIR@/lib/librte_node \
                           @TOPDIR@/lib/librte_net \
                           @TOPDIR@/lib/librte_pci \
                           @TOPDIR@/lib/librte_pdump \
diff --git a/lib/Makefile b/lib/Makefile
index 1f572b659..50d61a338 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -122,6 +122,9 @@ DEPDIRS-librte_rcu := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
 DEPDIRS-librte_graph := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_NODE) += librte_node
+DEPDIRS-librte_node := librte_graph librte_lpm librte_ethdev librte_mbuf
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
new file mode 100644
index 000000000..dbc8e1d44
--- /dev/null
+++ b/lib/librte_node/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_node.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
+LDLIBS += -lrte_eal -lrte_graph
+
+EXPORT_MAP := rte_node_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/log.c b/lib/librte_node/log.c
new file mode 100644
index 000000000..f035f91e8
--- /dev/null
+++ b/lib/librte_node/log.c
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "node_private.h"
+
+int rte_node_logtype;
+
+RTE_INIT(rte_node_init_log)
+{
+	rte_node_logtype = rte_log_register("lib.node");
+	if (rte_node_logtype >= 0)
+		rte_log_set_level(rte_node_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
new file mode 100644
index 000000000..a97813ad4
--- /dev/null
+++ b/lib/librte_node/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+sources = files('null.c', 'log.c')
+allow_experimental_apis = true
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
+deps += ['graph']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
new file mode 100644
index 000000000..f30902a94
--- /dev/null
+++ b/lib/librte_node/node_private.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __NODE_PRIVATE_H__
+#define __NODE_PRIVATE_H__
+
+#include <rte_common.h>
+#include <rte_log.h>
+
+extern int rte_node_logtype;
+#define NODE_LOG(level, node_name, ...)                                        \
+	rte_log(RTE_LOG_##level, rte_node_logtype,                             \
+		RTE_FMT("NODE %s: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",  \
+			node_name, __func__, __LINE__,                         \
+			RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define node_err(node_name, ...) NODE_LOG(ERR, node_name, __VA_ARGS__)
+#define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
+#define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
+
+#endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/null.c b/lib/librte_node/null.c
new file mode 100644
index 000000000..c7cd8b6df
--- /dev/null
+++ b/lib/librte_node/null.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_graph.h>
+
+static uint16_t
+null(struct rte_graph *graph, struct rte_node *node, void **objs,
+	uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(graph);
+
+	return nb_objs;
+}
+
+static struct rte_node_register null_node = {
+	.name = "null",
+	.process = null,
+};
+
+RTE_NODE_REGISTER(null_node);
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
new file mode 100644
index 000000000..f87163bb9
--- /dev/null
+++ b/lib/librte_node/rte_node_version.map
@@ -0,0 +1,6 @@
+EXPERIMENTAL {
+	global:
+
+	rte_node_logtype;
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index c43d86bb9..147129b0b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'graph', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'node', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
@@ -186,6 +186,9 @@ foreach l:libraries
 
 			dpdk_libraries = [shared_lib] + dpdk_libraries
 			dpdk_static_libraries = [static_lib] + dpdk_static_libraries
+			if libname == 'rte_node'
+				dpdk_graph_nodes = [static_lib]
+			endif
 		endif # sources.length() > 0
 
 		set_variable('shared_rte_' + name, shared_dep)
diff --git a/meson.build b/meson.build
index b7ae9c8d9..811c96421 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@ cc = meson.get_compiler('c')
 dpdk_conf = configuration_data()
 dpdk_libraries = []
 dpdk_static_libraries = []
+dpdk_graph_nodes = []
 dpdk_driver_classes = []
 dpdk_drivers = []
 dpdk_extra_ldflags = []
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index b1195f09a..68d7806a4 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -99,6 +99,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
 _LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
+_LDLIBS-$(CONFIG_RTE_LIBRTE_NODE)           += -lrte_node
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 16/28] node: add ethdev Rx node
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (14 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 15/28] node: add log infra and null node jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 17/28] node: add ethdev Tx node jerinj
                     ` (13 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add source rte_node ethdev_rx process function and register
it. This node is a source node that will be called periodically
and when called, performs rte_eth_rx_burst() on a specific
(port, queue) pair and enqueue them as stream of objects to
next node.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |   3 +-
 lib/librte_node/ethdev_rx.c      | 221 +++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_rx_priv.h |  81 +++++++++++
 lib/librte_node/meson.build      |   4 +-
 4 files changed, 306 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index dbc8e1d44..314149385 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,12 +11,13 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph
+LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_rx.c b/lib/librte_node/ethdev_rx.c
new file mode 100644
index 000000000..5cc736598
--- /dev/null
+++ b/lib/librte_node/ethdev_rx.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_rx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_rx_node_main ethdev_rx_main;
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process_inline(struct rte_graph *graph, struct rte_node *node,
+			      uint16_t port, uint16_t queue)
+{
+	uint16_t count, next_index = ETHDEV_RX_NEXT_IP4_LOOKUP;
+
+	/* Get pkts from port */
+	count = rte_eth_rx_burst(port, queue, (struct rte_mbuf **)node->objs,
+				 RTE_GRAPH_BURST_SIZE);
+
+	if (!count)
+		return 0;
+	node->idx = count;
+	/* Enqueue to next node */
+	rte_node_next_stream_move(graph, node, next_index);
+
+	return count;
+}
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t cnt)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	uint16_t n_pkts = 0;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(cnt);
+
+	n_pkts = ethdev_rx_node_process_inline(graph, node, ctx->port_id,
+					       ctx->queue_id);
+	return n_pkts;
+}
+
+static inline uint32_t
+l3_ptype(uint16_t etype, uint32_t ptype)
+{
+	ptype = ptype & ~RTE_PTYPE_L3_MASK;
+	if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
+		ptype |= RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
+	else if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6))
+		ptype |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	return ptype;
+}
+
+/* Callback for soft ptype parsing */
+static uint16_t
+eth_pkt_parse_cb(uint16_t port, uint16_t queue, struct rte_mbuf **mbufs,
+		 uint16_t nb_pkts, uint16_t max_pkts, void *user_param)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3;
+	struct rte_ether_hdr *eth_hdr;
+	uint16_t etype, n_left;
+	struct rte_mbuf **pkts;
+
+	RTE_SET_USED(port);
+	RTE_SET_USED(queue);
+	RTE_SET_USED(max_pkts);
+	RTE_SET_USED(user_param);
+
+	pkts = mbufs;
+	n_left = nb_pkts;
+	while (n_left >= 12) {
+
+		/* Prefetch next-next mbufs */
+		rte_prefetch0(pkts[8]);
+		rte_prefetch0(pkts[9]);
+		rte_prefetch0(pkts[10]);
+		rte_prefetch0(pkts[11]);
+
+		/* Prefetch next mbuf data */
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[4], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[5], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[6], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[7], struct rte_ether_hdr *));
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+		pkts += 4;
+		n_left -= 4;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf1 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf1->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf2 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf2->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf3 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf3->packet_type = l3_ptype(etype, 0);
+	}
+
+	while (n_left > 0) {
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left -= 1;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+	}
+
+	return nb_pkts;
+}
+
+#define MAX_PTYPES 16
+static int
+ethdev_ptype_setup(uint16_t port, uint16_t queue)
+{
+	uint8_t l3_ipv4 = 0, l3_ipv6 = 0;
+	uint32_t ptypes[MAX_PTYPES];
+	int i, rc;
+
+	/* Check IPv4 & IPv6 ptype support */
+	rc = rte_eth_dev_get_supported_ptypes(port, RTE_PTYPE_L3_MASK, ptypes,
+					      MAX_PTYPES);
+	for (i = 0; i < rc; i++) {
+		if (ptypes[i] & RTE_PTYPE_L3_IPV4)
+			l3_ipv4 = 1;
+		if (ptypes[i] & RTE_PTYPE_L3_IPV6)
+			l3_ipv6 = 1;
+	}
+
+	if (!l3_ipv4 || !l3_ipv6) {
+		node_info("ethdev_rx",
+			  "Enabling ptype callback for required ptypes on port %u\n",
+			  port);
+
+		if (!rte_eth_add_rx_callback(port, queue, eth_pkt_parse_cb,
+					     NULL)) {
+			node_err("ethdev_rx",
+				 "Failed to add rx ptype cb: port=%d, queue=%d\n",
+				 port, queue);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int
+ethdev_rx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	ethdev_rx_node_elem_t *elem = ethdev_rx_main.head;
+
+	RTE_SET_USED(graph);
+
+	while (elem) {
+		if (elem->nid == node->id) {
+			/* Update node specific context */
+			memcpy(ctx, &elem->ctx, sizeof(ethdev_rx_node_ctx_t));
+			break;
+		}
+		elem = elem->next;
+	}
+
+	RTE_VERIFY(elem != NULL);
+
+	/* Check and setup ptype */
+	return ethdev_ptype_setup(ctx->port_id, ctx->queue_id);
+}
+
+struct ethdev_rx_node_main *
+ethdev_rx_get_node_data_get(void)
+{
+	return &ethdev_rx_main;
+}
+
+static struct rte_node_register ethdev_rx_node_base = {
+	.process = ethdev_rx_node_process,
+	.flags = RTE_NODE_SOURCE_F,
+	.name = "ethdev_rx",
+
+	.init = ethdev_rx_node_init,
+
+	.nb_edges = ETHDEV_RX_NEXT_MAX,
+	.next_nodes = {[ETHDEV_RX_NEXT_IP4_LOOKUP] = "ip4_lookup"},
+};
+
+struct rte_node_register *
+ethdev_rx_node_get(void)
+{
+	return &ethdev_rx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_rx_node_base);
diff --git a/lib/librte_node/ethdev_rx_priv.h b/lib/librte_node/ethdev_rx_priv.h
new file mode 100644
index 000000000..2d7195a36
--- /dev/null
+++ b/lib/librte_node/ethdev_rx_priv.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_RX_PRIV_H__
+#define __INCLUDE_ETHDEV_RX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+struct ethdev_rx_node_elem;
+struct ethdev_rx_node_ctx;
+typedef struct ethdev_rx_node_elem ethdev_rx_node_elem_t;
+typedef struct ethdev_rx_node_ctx ethdev_rx_node_ctx_t;
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node context structure.
+ */
+struct ethdev_rx_node_ctx {
+	uint16_t port_id;  /**< Port identifier of the Rx node. */
+	uint16_t queue_id; /**< Queue identifier of the Rx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node list element structure.
+ */
+struct ethdev_rx_node_elem {
+	struct ethdev_rx_node_elem *next;
+	/**< Pointer to the next Rx node element. */
+	struct ethdev_rx_node_ctx ctx;
+	/**< Rx node context. */
+	rte_node_t nid;
+	/**< Node identifier of the Rx node. */
+};
+
+enum ethdev_rx_next_nodes {
+	ETHDEV_RX_NEXT_IP4_LOOKUP,
+	ETHDEV_RX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Rx node main structure.
+ */
+struct ethdev_rx_node_main {
+	ethdev_rx_node_elem_t *head;
+	/**< Pointer to the head Rx node element. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Rx node data.
+ */
+struct ethdev_rx_node_main *ethdev_rx_get_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Rx node.
+ */
+struct rte_node_register *ethdev_rx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_RX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index a97813ad4..94caa6c23 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph']
+deps += ['graph', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 17/28] node: add ethdev Tx node
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (15 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 16/28] node: add ethdev Rx node jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 18/28] node: add ethdev Rx and Tx node ctrl API jerinj
                     ` (12 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add rte_node ethdev_tx process function and register it to
graph infra. This node has a specific (port, tx-queue) as context
and it enqueue's all the packets received to that specific queue pair.
When rte_eth_tx_burst() i.e enqueue to queue pair fails, packets
are forwarded to pkt_drop node to be free'd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |  3 +-
 lib/librte_node/ethdev_tx.c      | 86 ++++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_tx_priv.h | 62 +++++++++++++++++++++++
 lib/librte_node/meson.build      |  4 +-
 4 files changed, 152 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 314149385..7428f6c43 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
@@ -19,5 +19,6 @@ EXPORT_MAP := rte_node_version.map
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_tx.c b/lib/librte_node/ethdev_tx.c
new file mode 100644
index 000000000..075149089
--- /dev/null
+++ b/lib/librte_node/ethdev_tx.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_tx_priv.h"
+
+static struct ethdev_tx_node_main ethdev_tx_main;
+
+static uint16_t
+ethdev_tx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t nb_objs)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint16_t port, queue;
+	uint16_t count;
+
+	/* Get Tx port id */
+	port = ctx->port;
+	queue = ctx->queue;
+
+	count = rte_eth_tx_burst(port, queue, (struct rte_mbuf **)objs,
+				 nb_objs);
+
+	/* Redirect unsent pkts to drop node */
+	if (count != nb_objs) {
+		rte_node_enqueue(graph, node, ETHDEV_TX_NEXT_PKT_DROP,
+				 &objs[count], nb_objs - count);
+	}
+
+	return count;
+}
+
+static int
+ethdev_tx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint64_t port_id = RTE_MAX_ETHPORTS;
+	int i;
+
+	/* Find our port id */
+	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+		if (ethdev_tx_main.nodes[i] == node->id) {
+			port_id = i;
+			break;
+		}
+	}
+	RTE_VERIFY(port_id < RTE_MAX_ETHPORTS);
+
+	/* Update port and queue */
+	ctx->port = port_id;
+	ctx->queue = graph->id;
+
+	return 0;
+}
+
+struct ethdev_tx_node_main *
+ethdev_tx_node_data_get(void)
+{
+	return &ethdev_tx_main;
+}
+
+static struct rte_node_register ethdev_tx_node_base = {
+	.process = ethdev_tx_node_process,
+	.name = "ethdev_tx",
+
+	.init = ethdev_tx_node_init,
+
+	.nb_edges = ETHDEV_TX_NEXT_MAX,
+	.next_nodes = {
+		[ETHDEV_TX_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+struct rte_node_register *
+ethdev_tx_node_get(void)
+{
+	return &ethdev_tx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_tx_node_base);
diff --git a/lib/librte_node/ethdev_tx_priv.h b/lib/librte_node/ethdev_tx_priv.h
new file mode 100644
index 000000000..586bff44a
--- /dev/null
+++ b/lib/librte_node/ethdev_tx_priv.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_TX_PRIV_H__
+#define __INCLUDE_ETHDEV_TX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ethdev_tx_node_ctx;
+typedef struct ethdev_tx_node_ctx ethdev_tx_node_ctx_t;
+
+enum ethdev_tx_next_nodes {
+	ETHDEV_TX_NEXT_PKT_DROP,
+	ETHDEV_TX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node context structure.
+ */
+struct ethdev_tx_node_ctx {
+	uint16_t port;	/**< Port identifier of the Ethernet Tx node. */
+	uint16_t queue; /**< Queue identifier of the Ethernet Tx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node main structure.
+ */
+struct ethdev_tx_node_main {
+	uint32_t nodes[RTE_MAX_ETHPORTS]; /**< Tx nodes for each ethdev port. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Tx node data.
+ */
+struct ethdev_tx_node_main *ethdev_tx_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Tx node.
+ */
+struct rte_node_register *ethdev_tx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_TX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 94caa6c23..505c76abd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'ethdev']
+deps += ['graph', 'mbuf', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 18/28] node: add ethdev Rx and Tx node ctrl API
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (16 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 17/28] node: add ethdev Tx node jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 19/28] node: ipv4 lookup for arm64 jerinj
                     ` (11 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ctrl api to setup ethdev_rx and ethdev_tx node.
This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
nodes with specific (port, queue) pairs updated in their context.
All the ethdev ports and queues are setup before this api
is called.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 doc/api/doxy-api-index.md            |   2 +
 lib/librte_node/Makefile             |   6 +-
 lib/librte_node/ethdev_ctrl.c        | 100 +++++++++++++++++++++++++++
 lib/librte_node/meson.build          |   5 +-
 lib/librte_node/node_private.h       |  74 ++++++++++++++++++++
 lib/librte_node/rte_node_eth_api.h   |  70 +++++++++++++++++++
 lib/librte_node/rte_node_version.map |   1 +
 7 files changed, 255 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index fd2ff64d7..1784674df 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -161,6 +161,8 @@ The public API headers are grouped by topics:
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
     [graph_worker]     (@ref rte_graph_worker.h)
+  * graph_nodes:
+    [eth_node]         (@ref rte_node_eth_api.h),
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 7428f6c43..ea5fa77f7 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -20,5 +20,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
new file mode 100644
index 000000000..b2ac5e2c4
--- /dev/null
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+
+#include "rte_node_eth_api.h"
+
+#include "ethdev_rx_priv.h"
+#include "ethdev_tx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_ctrl {
+	uint16_t nb_graphs;
+} ctrl;
+
+int
+rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
+		    uint16_t nb_graphs)
+{
+	struct ethdev_tx_node_main *tx_node_data;
+	uint16_t tx_q_used, rx_q_used, port_id;
+	struct rte_node_register *tx_node;
+	char name[RTE_NODE_NAMESIZE];
+	struct rte_mempool *mp;
+	uint32_t id;
+	int i, j;
+
+	tx_node_data = ethdev_tx_node_data_get();
+	tx_node = ethdev_tx_node_get();
+	for (i = 0; i < nb_confs; i++) {
+		port_id = conf[i].port_id;
+
+		if (!rte_eth_dev_is_valid_port(port_id))
+			return -EINVAL;
+
+		/* Check for mbuf minimum private size requirement */
+		for (j = 0; j < conf[i].mp_count; j++) {
+			mp = conf[i].mp[j];
+			if (!mp)
+				continue;
+			/* Check for minimum private space */
+			if (rte_pktmbuf_priv_size(mp) <
+			    RTE_NODE_MBUF_PRIV2_SIZE) {
+				node_err("ethdev",
+					 "Minimum mbuf priv size requirement not met by mp %s",
+					 mp->name);
+				return -EINVAL;
+			}
+		}
+
+		rx_q_used = conf[i].num_rx_queues;
+		tx_q_used = conf[i].num_tx_queues;
+		/* Check if we have a txq for each worker */
+		if (tx_q_used < nb_graphs)
+			return -EINVAL;
+
+		/* Create node for each rx port queue pair */
+		for (j = 0; j < rx_q_used; j++) {
+			struct ethdev_rx_node_main *rx_node_data;
+			struct rte_node_register *rx_node;
+			ethdev_rx_node_elem_t *elem;
+
+			rx_node_data = ethdev_rx_get_node_data_get();
+			rx_node = ethdev_rx_node_get();
+			snprintf(name, sizeof(name), "%u-%u", port_id, j);
+			/* Clone a new rx node with same edges as parent */
+			id = rte_node_clone(rx_node->id, name);
+			if (id == RTE_NODE_ID_INVALID)
+				return -EIO;
+
+			/* Add it to list of ethdev rx nodes for lookup */
+			elem = malloc(sizeof(ethdev_rx_node_elem_t));
+			memset(elem, 0, sizeof(ethdev_rx_node_elem_t));
+			elem->ctx.port_id = port_id;
+			elem->ctx.queue_id = j;
+			elem->nid = id;
+			elem->next = rx_node_data->head;
+			rx_node_data->head = elem;
+
+			node_dbg("ethdev", "Rx node %s-%s: is at %u",
+				 rx_node->name, name, id);
+		}
+
+		/* Create a per port tx node from base node */
+		snprintf(name, sizeof(name), "%u", port_id);
+		/* Clone a new node with same edges as parent */
+		id = rte_node_clone(tx_node->id, name);
+		tx_node_data->nodes[port_id] = id;
+
+		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
+			 name, id);
+	}
+
+	ctrl.nb_graphs = nb_graphs;
+	return 0;
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 505c76abd..af2eb2d91 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
+headers = files('rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'mbuf', 'ethdev']
+deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
index f30902a94..141448546 100644
--- a/lib/librte_node/node_private.h
+++ b/lib/librte_node/node_private.h
@@ -6,7 +6,9 @@
 #define __NODE_PRIVATE_H__
 
 #include <rte_common.h>
+#include <rte_crypto.h>
 #include <rte_log.h>
+#include <rte_mbuf.h>
 
 extern int rte_node_logtype;
 #define NODE_LOG(level, node_name, ...)                                        \
@@ -19,4 +21,76 @@ extern int rte_node_logtype;
 #define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
 #define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store next hop, ttl and checksum.
+ */
+struct rte_node_mbuf_priv1 {
+	union {
+		/* IP4 rewrite */
+		struct {
+			uint16_t nh;
+			uint16_t ttl;
+			uint32_t cksum;
+		};
+
+		uint64_t u;
+	};
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store crypto operation.
+ */
+struct rte_node_mbuf_priv2 {
+	union {
+		/* Sym crypto */
+		struct {
+			struct rte_crypto_op op;
+		};
+	};
+} __rte_cache_aligned;
+
+#define RTE_NODE_MBUF_PRIV2_SIZE sizeof(struct rte_node_mbuf_priv2)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv1 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv1.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv1 *
+rte_node_mbuf_priv1(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv1 *)&m->udata64;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv2 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv2.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv2 *
+rte_node_mbuf_priv2(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv2 *)rte_mbuf_to_priv(m);
+}
+
 #endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/rte_node_eth_api.h b/lib/librte_node/rte_node_eth_api.h
new file mode 100644
index 000000000..39b31b45b
--- /dev/null
+++ b/lib/librte_node/rte_node_eth_api.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_ETH_API_H__
+#define __INCLUDE_RTE_NODE_ETH_API_H__
+
+/**
+ * @file rte_node_eth_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to setup ethdev_rx and ethdev_tx nodes
+ * and its queue associations.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_mempool.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Port config for ethdev_rx and ethdev_tx node.
+ */
+struct rte_node_ethdev_config {
+	uint16_t port_id;
+	/**< Port identifier */
+	uint16_t num_rx_queues;
+	/**< Number of Rx queues. */
+	uint16_t num_tx_queues;
+	/**< Number of Tx queues. */
+	struct rte_mempool **mp;
+	/**< Array of mempools associated to Rx queue. */
+	uint16_t mp_count;
+	/**< Size of mp array. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Initializes ethdev nodes.
+ *
+ * @param cfg
+ *   Array of ethdev config that identifies which port's
+ *   ethdev_rx and ethdev_tx nodes need to be created
+ *   and queue association.
+ * @param cnt
+ *   Size of cfg array.
+ * @param nb_graphs
+ *   Number of graphs that will be used.
+ *
+ * @return
+ *   0 on successful initialization, negative otherwise.
+ */
+__rte_experimental
+int rte_node_eth_config(struct rte_node_ethdev_config *cfg,
+			uint16_t cnt, uint16_t nb_graphs);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_ETH_API_H__ */
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index f87163bb9..c6c71bd02 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -1,6 +1,7 @@
 EXPERIMENTAL {
 	global:
 
+	rte_node_eth_config;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 19/28] node: ipv4 lookup for arm64
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (17 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 18/28] node: add ethdev Rx and Tx node ctrl API jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 20/28] node: ipv4 lookup for x86 jerinj
                     ` (10 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add arm64 specific IPv4 lookup process function
for ip4_lookup node. This node performs LPM lookup
on every packet received and forwards it to a next
node that is identified by lookup result.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 doc/api/doxy-api-index.md          |   1 +
 lib/librte_node/Makefile           |   4 +-
 lib/librte_node/ip4_lookup.c       | 301 +++++++++++++++++++++++++++++
 lib/librte_node/meson.build        |   5 +-
 lib/librte_node/rte_node_ip4_api.h |  43 +++++
 5 files changed, 351 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/rte_node_ip4_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 1784674df..3908fddde 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -163,6 +163,7 @@ The public API headers are grouped by topics:
     [graph_worker]     (@ref rte_graph_worker.h)
   * graph_nodes:
     [eth_node]         (@ref rte_node_eth_api.h),
+    [ip4_node]         (@ref rte_node_ip4_api.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ea5fa77f7..ad3f2e349 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_lpm -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -21,8 +21,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 
 # install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
new file mode 100644
index 000000000..dddabc6c2
--- /dev/null
+++ b/lib/librte_node/ip4_lookup.c
@@ -0,0 +1,301 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_lpm.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "node_private.h"
+
+#define IPV4_L3FWD_LPM_MAX_RULES 1024
+#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
+
+/* IP4 Lookup global data struct */
+struct ip4_lookup_node_main {
+	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
+};
+
+#if defined(RTE_MACHINE_CPUFLAG_NEON)
+/* ARM64 NEON */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	rte_edge_t next_index;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t result;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int32x4_t dip;
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+#define OBJS_PER_CLINE (RTE_CACHE_LINE_SIZE / sizeof(void *))
+	for (i = OBJS_PER_CLINE; i < RTE_GRAPH_BURST_SIZE; i += OBJS_PER_CLINE)
+		rte_prefetch0(&objs[i]);
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+#if RTE_GRAPH_BURST_SIZE > 64
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+#endif
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 0);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[1] = ipv4_hdr->time_to_live;
+		priv01.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 1);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[5] = ipv4_hdr->time_to_live;
+		priv01.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 2);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[1] = ipv4_hdr->time_to_live;
+		priv23.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 3);
+
+		dip = vreinterpretq_s32_u8(
+			vrev32q_u8(vreinterpretq_u8_s32(dip)));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[5] = ipv4_hdr->time_to_live;
+		priv23.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, result.u32, drop_nh);
+		priv01.u16[0] = result.u16[0];
+		priv01.u16[4] = result.u16[2];
+		priv23.u16[0] = result.u16[4];
+		priv23.u16[4] = result.u16[6];
+
+		rte_node_mbuf_priv1(mbuf0)->u = priv01.u64[0];
+		rte_node_mbuf_priv1(mbuf1)->u = priv01.u64[1];
+		rte_node_mbuf_priv1(mbuf2)->u = priv23.u64[0];
+		rte_node_mbuf_priv1(mbuf3)->u = priv23.u64[1];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec = ((next_index == result.u16[1]) &&
+				       (result.u16[1] == result.u16[3]) &&
+				       (result.u16[3] == result.u16[5]) &&
+				       (result.u16[5] == result.u16[7]));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == result.u16[1]) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[1],
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == result.u16[3]) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[3],
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == result.u16[5]) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[5],
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == result.u16[7]) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[7],
+						    from[3]);
+			}
+
+			from += 4;
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+		uint16_t next0;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next0 = (uint16_t)next_hop;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#else
+
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+	return nb_objs;
+}
+
+#endif
+
+static int
+ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+
+	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_lookup_node = {
+	.process = ip4_lookup_node_process,
+	.name = "ip4_lookup",
+
+	.init = ip4_lookup_node_init,
+
+	.nb_edges = RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	.next_nodes = {
+		[RTE_NODE_IP4_LOOKUP_NEXT_REWRITE] = "ip4_rewrite",
+		[RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(ip4_lookup_node);
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index af2eb2d91..702d21a24 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
-headers = files('rte_node_eth_api.h')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
+		'ethdev_ctrl.c')
+headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
new file mode 100644
index 000000000..37c12bf82
--- /dev/null
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_IP4_API_H__
+#define __INCLUDE_RTE_NODE_IP4_API_H__
+
+/**
+ * @file rte_node_ip4_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to do control path functions of ip4_* nodes
+ * like ip4_lookup, ip4_rewrite.
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * IP4 lookup next nodes.
+ */
+enum rte_node_ip4_lookup_next {
+	RTE_NODE_IP4_LOOKUP_NEXT_REWRITE,
+	/**< Rewrite node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP,
+	/**< Packet drop node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	/**< Number of next nodes of lookup node. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_IP4_API_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 20/28] node: ipv4 lookup for x86
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (18 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 19/28] node: ipv4 lookup for arm64 jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-27  8:40     ` Jerin Jacob
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 21/28] node: add ipv4 rewrite node jerinj
                     ` (9 subsequent siblings)
  29 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup
rte_node. This node performs LPM lookup using x86_64
vector supported RTE_LPM API on every packet received
and forwards it to a next node that is identified by
lookup result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ip4_lookup.c | 238 +++++++++++++++++++++++++++++++++++
 1 file changed, 238 insertions(+)

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index dddabc6c2..a9e204148 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -259,6 +259,244 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 	return nb_objs;
 }
 
+#elif defined(RTE_ARCH_X86)
+
+/* X86 SSE */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	rte_edge_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	uint32_t ip0, ip1, ip2, ip3;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t dst;
+	__m128i dip; /* SSE register */
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	if (n_left_from >= 4) {
+		for (i = 0; i < 4; i++)
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+	}
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip0 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip1 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip2 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip3 = ipv4_hdr->dst_addr;
+
+		/* Prepare for lookup x4 */
+		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
+
+		/* Byte swap 4 IPV4 addresses. */
+		const __m128i bswap_mask = _mm_set_epi8(
+			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
+		dip = _mm_shuffle_epi8(dip, bswap_mask);
+
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr->time_to_live;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
+
+		/* Extract next node id and NH */
+		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] & 0xFFFF;
+		next0 = (dst.u32[0] >> 16);
+
+		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] & 0xFFFF;
+		next1 = (dst.u32[1] >> 16);
+
+		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] & 0xFFFF;
+		next2 = (dst.u32[2] >> 16);
+
+		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] & 0xFFFF;
+		next3 = (dst.u32[3] >> 16);
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			(next_index ^ next0) | (next_index ^ next1) |
+			(next_index ^ next2) | (next_index ^ next3);
+
+		if (unlikely(fix_spec)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = next_hop & 0xFFFF;
+		next0 = (next_hop >> 16);
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	/* Copy things successfully speculated till now */
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
 #else
 
 static uint16_t
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 21/28] node: add ipv4 rewrite node
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (19 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 20/28] node: ipv4 lookup for x86 jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 22/28] node: add ipv4 rewrite and lookup ctrl API jerinj
                     ` (8 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Kiran Kumar K <kirankumark@marvell.com>

Add ip4 rewrite process function for ip4_rewrite
rte_node. On every packet received by this node,
header is overwritten with new data before forwarding
it to next node. Header data to overwrite with is
identified by next hop id passed in mbuf priv data
by previous node.

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_node/Makefile           |   1 +
 lib/librte_node/ip4_rewrite.c      | 270 +++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h |  55 ++++++
 lib/librte_node/meson.build        |   2 +-
 4 files changed, 327 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ad3f2e349..ebf473c66 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -22,6 +22,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
new file mode 100644
index 000000000..ef49ccea0
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite.c
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_vect.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "ip4_rewrite_priv.h"
+#include "node_private.h"
+
+static struct ip4_rewrite_node_main *ip4_rewrite_nm;
+
+static uint16_t
+ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
+			 void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct ip4_rewrite_nh_header *nh = ip4_rewrite_nm->nh;
+	uint16_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ip0, *ip1, *ip2, *ip3;
+	uint16_t n_left_from, held = 0, last_spec = 0;
+	void *d0, *d1, *d2, *d3;
+	void **to_next, **from;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int i;
+
+	/* Speculative next as last next */
+	next_index = *(uint16_t *)node->ctx;
+	rte_prefetch0(nh);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(pkts[i]);
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	/* Update Ethernet header of pkts */
+	while (n_left_from >= 4) {
+		if (likely(n_left_from > 7)) {
+			/* Prefetch only next-mbuf struct and priv area.
+			 * Data need not be prefetched as we only write.
+			 */
+			rte_prefetch0(pkts[4]);
+			rte_prefetch0(pkts[5]);
+			rte_prefetch0(pkts[6]);
+			rte_prefetch0(pkts[7]);
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+		priv01.u64[0] = rte_node_mbuf_priv1(mbuf0)->u;
+		priv01.u64[1] = rte_node_mbuf_priv1(mbuf1)->u;
+		priv23.u64[0] = rte_node_mbuf_priv1(mbuf2)->u;
+		priv23.u64[1] = rte_node_mbuf_priv1(mbuf3)->u;
+
+		/* Increment checksum by one. */
+		priv01.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv01.u32[3] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[3] += rte_cpu_to_be_16(0x0100);
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf0 */
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[priv01.u16[0]].rewrite_data,
+			   nh[priv01.u16[0]].rewrite_len);
+
+		next0 = nh[priv01.u16[0]].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		ip0->time_to_live = priv01.u16[1] - 1;
+		ip0->hdr_checksum = priv01.u16[2] + priv01.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf1 */
+		d1 = rte_pktmbuf_mtod(mbuf1, void *);
+		rte_memcpy(d1, nh[priv01.u16[4]].rewrite_data,
+			   nh[priv01.u16[4]].rewrite_len);
+
+		next1 = nh[priv01.u16[4]].tx_node;
+		ip1 = (struct rte_ipv4_hdr *)((uint8_t *)d1 +
+					      sizeof(struct rte_ether_hdr));
+		ip1->time_to_live = priv01.u16[5] - 1;
+		ip1->hdr_checksum = priv01.u16[6] + priv01.u16[7];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf2 */
+		d2 = rte_pktmbuf_mtod(mbuf2, void *);
+		rte_memcpy(d2, nh[priv23.u16[0]].rewrite_data,
+			   nh[priv23.u16[0]].rewrite_len);
+		next2 = nh[priv23.u16[0]].tx_node;
+		ip2 = (struct rte_ipv4_hdr *)((uint8_t *)d2 +
+					      sizeof(struct rte_ether_hdr));
+		ip2->time_to_live = priv23.u16[1] - 1;
+		ip2->hdr_checksum = priv23.u16[2] + priv23.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf3 */
+		d3 = rte_pktmbuf_mtod(mbuf3, void *);
+		rte_memcpy(d3, nh[priv23.u16[4]].rewrite_data,
+			   nh[priv23.u16[4]].rewrite_len);
+
+		next3 = nh[priv23.u16[4]].tx_node;
+		ip3 = (struct rte_ipv4_hdr *)((uint8_t *)d3 +
+					      sizeof(struct rte_ether_hdr));
+		ip3->time_to_live = priv23.u16[5] - 1;
+		ip3->hdr_checksum = priv23.u16[6] + priv23.u16[7];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			((next_index == next0) && (next0 == next1) &&
+			 (next1 == next2) && (next2 == next3));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+			/* Change speculation if last two are same */
+			if ((next_index != next3) && (next2 == next3)) {
+				/* Put the current speculated node */
+				rte_node_next_stream_put(graph, node,
+							 next_index, held);
+				held = 0;
+
+				/* Get next speculated stream */
+				next_index = next3;
+				to_next = rte_node_next_stream_get(
+					graph, node, next_index, nb_objs);
+			}
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint16_t chksum;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_data,
+			   nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_len);
+
+		next0 = nh[rte_node_mbuf_priv1(mbuf0)->nh].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		chksum = rte_node_mbuf_priv1(mbuf0)->cksum +
+			 rte_cpu_to_be_16(0x0100);
+		chksum += chksum >= 0xffff;
+		ip0->hdr_checksum = chksum;
+		ip0->time_to_live = rte_node_mbuf_priv1(mbuf0)->ttl - 1;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+	/* Save the last next used */
+	*(uint16_t *)node->ctx = next_index;
+
+	return nb_objs;
+}
+
+static int
+ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_rewrite_node = {
+	.process = ip4_rewrite_node_process,
+	.name = "ip4_rewrite",
+	/* Default edge i.e '0' is pkt drop */
+	.nb_edges = 1,
+	.next_nodes = {
+		[0] = "pkt_drop",
+	},
+	.init = ip4_rewrite_node_init,
+};
+
+RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
new file mode 100644
index 000000000..420996a03
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_IP4_REWRITE_PRIV_H__
+#define __INCLUDE_IP4_REWRITE_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+#define RTE_GRAPH_IP4_REWRITE_MAX_NH 64
+#define RTE_GRAPH_IP4_REWRITE_MAX_LEN 56
+
+/**
+ * @internal
+ *
+ * Ipv4 rewrite next hop header data structure. Used to store port specific
+ * rewrite data.
+ */
+struct ip4_rewrite_nh_header {
+	uint16_t rewrite_len; /**< Header rewrite length. */
+	uint16_t tx_node;     /**< Tx node next index identifier. */
+	uint16_t enabled;     /**< NH enable flag */
+	uint16_t rsvd;
+	union {
+		struct {
+			struct rte_ether_addr dst;
+			/**< Destination mac address. */
+			struct rte_ether_addr src;
+			/**< Source mac address. */
+		};
+		uint8_t rewrite_data[RTE_GRAPH_IP4_REWRITE_MAX_LEN];
+		/**< Generic rewrite data */
+	};
+};
+
+/**
+ * @internal
+ *
+ * Ipv4 node main data structure.
+ */
+struct ip4_rewrite_node_main {
+	struct ip4_rewrite_nh_header nh[RTE_GRAPH_IP4_REWRITE_MAX_NH];
+	/**< Array of next hop header data */
+	uint16_t next_index[RTE_MAX_ETHPORTS];
+	/**< Next index of each configured port. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_IP4_REWRITE_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 702d21a24..ed78791dd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 22/28] node: add ipv4 rewrite and lookup ctrl API
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (20 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 21/28] node: add ipv4 rewrite node jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 23/28] node: add pkt drop node jerinj
                     ` (7 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ip4_rewrite and ip4_lookup ctrl API. ip4_lookup ctrl
API is used to add route entries for LPM lookup with
result data containing next hop id and next proto.
ip4_rewrite ctrl API is used to add rewrite data for
every next hop.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ethdev_ctrl.c        | 18 ++++++-
 lib/librte_node/ip4_lookup.c         | 80 ++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite.c        | 56 +++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h   | 22 ++++++++
 lib/librte_node/rte_node_ip4_api.h   | 44 +++++++++++++++
 lib/librte_node/rte_node_version.map |  2 +
 6 files changed, 221 insertions(+), 1 deletion(-)

diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
index b2ac5e2c4..845d92987 100644
--- a/lib/librte_node/ethdev_ctrl.c
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -11,6 +11,7 @@
 
 #include "ethdev_rx_priv.h"
 #include "ethdev_tx_priv.h"
+#include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
 static struct ethdev_ctrl {
@@ -21,14 +22,17 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 		    uint16_t nb_graphs)
 {
+	struct rte_node_register *ip4_rewrite_node;
 	struct ethdev_tx_node_main *tx_node_data;
 	uint16_t tx_q_used, rx_q_used, port_id;
 	struct rte_node_register *tx_node;
 	char name[RTE_NODE_NAMESIZE];
+	const char *next_nodes = name;
 	struct rte_mempool *mp;
+	int i, j, rc;
 	uint32_t id;
-	int i, j;
 
+	ip4_rewrite_node = ip4_rewrite_node_get();
 	tx_node_data = ethdev_tx_node_data_get();
 	tx_node = ethdev_tx_node_get();
 	for (i = 0; i < nb_confs; i++) {
@@ -93,6 +97,18 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 
 		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
 			 name, id);
+
+		/* Prepare the actual name of the cloned node */
+		snprintf(name, sizeof(name), "ethdev_tx-%u", port_id);
+
+		/* Add this tx port node as next to ip4_rewrite_node */
+		rte_node_edge_update(ip4_rewrite_node->id, RTE_EDGE_ID_INVALID,
+				     &next_nodes, 1);
+		/* Assuming edge id is the last one alloc'ed */
+		rc = ip4_rewrite_set_next(
+			port_id, rte_node_edge_count(ip4_rewrite_node->id) - 1);
+		if (rc < 0)
+			return rc;
 	}
 
 	ctrl.nb_graphs = nb_graphs;
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index a9e204148..0665d1ea5 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -28,6 +28,8 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+static struct ip4_lookup_node_main ip4_lookup_nm;
+
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 /* ARM64 NEON */
 static uint16_t
@@ -512,12 +514,90 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 
 #endif
 
+int
+rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+		       enum rte_node_ip4_lookup_next next_node)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	struct in_addr in;
+	uint8_t socket;
+	uint32_t val;
+	int ret;
+
+	in.s_addr = htonl(ip);
+	inet_ntop(AF_INET, &in, abuf, sizeof(abuf));
+	/* Embedded next node id in next hop */
+	val = (next_node << 16) | next_hop;
+	node_dbg("ip4_lookup", "LPM: Adding route %s / %d nh (0x%x)", abuf,
+		 depth, val);
+
+	for (socket = 0; socket < RTE_MAX_NUMA_NODES; socket++) {
+		if (!ip4_lookup_nm.lpm_tbl[socket])
+			continue;
+
+		ret = rte_lpm_add(ip4_lookup_nm.lpm_tbl[socket], ip, depth,
+				  val);
+
+		if (ret < 0) {
+			node_err("ip4_lookup",
+				 "Unable to add entry %s / %d nh (%x) to LPM table on sock %d, rc=%d\n",
+				 abuf, depth, val, socket, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int
+setup_lpm(struct ip4_lookup_node_main *nm, int socket)
+{
+	struct rte_lpm_config config_ipv4;
+	char s[RTE_LPM_NAMESIZE];
+
+	/* One LPM table per socket */
+	if (nm->lpm_tbl[socket])
+		return 0;
+
+	/* create the LPM table */
+	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
+	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
+	config_ipv4.flags = 0;
+	snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socket);
+	nm->lpm_tbl[socket] = rte_lpm_create(s, socket, &config_ipv4);
+	if (nm->lpm_tbl[socket] == NULL)
+		return -rte_errno;
+
+	return 0;
+}
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
+	struct rte_lpm **lpm_p = (struct rte_lpm **)&node->ctx;
+	uint16_t socket, lcore_id;
+	static uint8_t init_once;
+	int rc;
+
 	RTE_SET_USED(graph);
 	RTE_SET_USED(node);
 
+	if (!init_once) {
+		/* Setup LPM tables for all sockets */
+		RTE_LCORE_FOREACH(lcore_id)
+		{
+			socket = rte_lcore_to_socket_id(lcore_id);
+			rc = setup_lpm(&ip4_lookup_nm, socket);
+			if (rc) {
+				node_err("ip4_lookup",
+					 "Failed to setup lpm tbl for sock %u, rc=%d",
+					 socket, rc);
+				return rc;
+			}
+		}
+		init_once = 1;
+	}
+	*lpm_p = ip4_lookup_nm.lpm_tbl[graph->socket];
 	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
 
 	return 0;
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
index ef49ccea0..5663f1eb1 100644
--- a/lib/librte_node/ip4_rewrite.c
+++ b/lib/librte_node/ip4_rewrite.c
@@ -256,6 +256,56 @@ ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 	return 0;
 }
 
+int
+ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index)
+{
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+	ip4_rewrite_nm->next_index[port_id] = next_index;
+
+	return 0;
+}
+
+int
+rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			 uint8_t rewrite_len, uint16_t dst_port)
+{
+	struct ip4_rewrite_nh_header *nh;
+
+	if (next_hop >= RTE_GRAPH_IP4_REWRITE_MAX_NH)
+		return -EINVAL;
+
+	if (rewrite_len > RTE_GRAPH_IP4_REWRITE_MAX_LEN)
+		return -EINVAL;
+
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+
+	/* Check if dst port doesn't exist as edge */
+	if (!ip4_rewrite_nm->next_index[dst_port])
+		return -EINVAL;
+
+	/* Update next hop */
+	nh = &ip4_rewrite_nm->nh[next_hop];
+
+	memcpy(nh->rewrite_data, rewrite_data, rewrite_len);
+	nh->tx_node = ip4_rewrite_nm->next_index[dst_port];
+	nh->rewrite_len = rewrite_len;
+	nh->enabled = true;
+
+	return 0;
+}
+
 static struct rte_node_register ip4_rewrite_node = {
 	.process = ip4_rewrite_node_process,
 	.name = "ip4_rewrite",
@@ -267,4 +317,10 @@ static struct rte_node_register ip4_rewrite_node = {
 	.init = ip4_rewrite_node_init,
 };
 
+struct rte_node_register *
+ip4_rewrite_node_get(void)
+{
+	return &ip4_rewrite_node;
+}
+
 RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
index 420996a03..80f0abdc9 100644
--- a/lib/librte_node/ip4_rewrite_priv.h
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -48,6 +48,28 @@ struct ip4_rewrite_node_main {
 	/**< Next index of each configured port. */
 };
 
+/**
+ * @internal
+ *
+ * Get the ipv4 rewrite node.
+ *
+ * @retrun
+ *   Pointer to the ipv4 rewrite node.
+ */
+struct rte_node_register *ip4_rewrite_node_get(void);
+
+/**
+ * @internal
+ *
+ * Set the Edge index of a given port_id.
+ *
+ * @param port_id
+ *   Ethernet port identifier.
+ * @param next_index
+ *   Edge index of the Given Tx node.
+ */
+int ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
index 37c12bf82..394cac097 100644
--- a/lib/librte_node/rte_node_ip4_api.h
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -36,6 +36,50 @@ enum rte_node_ip4_lookup_next {
 	/**< Number of next nodes of lookup node. */
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add ipv4 route to lookup table.
+ *
+ * @param ip
+ *   IP address of route to be added.
+ * @param depth
+ *   Depth of the rule to be added.
+ * @param next_hop
+ *   Next hop id of the rule result to be added.
+ * @param next_node
+ *   Next node to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+			   enum rte_node_ip4_lookup_next next_node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add a next hop's rewrite data.
+ *
+ * @param next_hop
+ *   Next hop id to add rewrite data to.
+ * @param rewrite_data
+ *   Rewrite data.
+ * @param rewrite_len
+ *   Length of rewrite data.
+ * @param dst_port
+ *   Destination port to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			     uint8_t rewrite_len, uint16_t dst_port);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index c6c71bd02..a799b0d38 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -2,6 +2,8 @@ EXPERIMENTAL {
 	global:
 
 	rte_node_eth_config;
+	rte_node_ip4_route_add;
+	rte_node_ip4_rewrite_add;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v2 23/28] node: add pkt drop node
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (21 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 22/28] node: add ipv4 rewrite and lookup ctrl API jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 24/28] l3fwd-graph: add graph based l3fwd skeleton jerinj
                     ` (6 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add pkt drop node process function for pkt_drop
rte_node. This node simply free's every object received as
an rte_mbuf to its rte_pktmbuf pool.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile    |  1 +
 lib/librte_node/meson.build |  2 +-
 lib/librte_node/pkt_drop.c  | 26 ++++++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/pkt_drop.c

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ebf473c66..322b651c8 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -23,6 +23,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += pkt_drop.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index ed78791dd..8aa93654b 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ip4_rewrite.c', 'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'pkt_drop.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/librte_node/pkt_drop.c b/lib/librte_node/pkt_drop.c
new file mode 100644
index 000000000..c35001323
--- /dev/null
+++ b/lib/librte_node/pkt_drop.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_graph.h>
+#include <rte_mbuf.h>
+
+static uint16_t
+pkt_drop_process(struct rte_graph *graph, struct rte_node *node, void **objs,
+		 uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(graph);
+
+	rte_pktmbuf_free_bulk((struct rte_mbuf **)objs, nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register pkt_drop_node = {
+	.process = pkt_drop_process,
+	.name = "pkt_drop",
+};
+
+RTE_NODE_REGISTER(pkt_drop_node);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 24/28] l3fwd-graph: add graph based l3fwd skeleton
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (22 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 23/28] node: add pkt drop node jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 25/28] l3fwd-graph: add ethdev configuration changes jerinj
                     ` (5 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Thomas Monjalon, Marko Kovacevic, Ori Kam, Bruce Richardson,
	Radu Nicolau, Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori,
	Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph based l3fwd application skeleton with cmdline
parsing support inline with normal l3fwd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                      |   3 +
 examples/Makefile                |   3 +
 examples/l3fwd-graph/Makefile    |  58 ++++
 examples/l3fwd-graph/main.c      | 525 +++++++++++++++++++++++++++++++
 examples/l3fwd-graph/meson.build |  13 +
 examples/meson.build             |   6 +-
 6 files changed, 606 insertions(+), 2 deletions(-)
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index c1acaedab..1d2cf6caa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1581,6 +1581,9 @@ F: doc/guides/sample_app_ug/l2_forward_event.rst
 F: examples/l3fwd/
 F: doc/guides/sample_app_ug/l3_forward.rst
 
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+F: examples/l3fwd-graph/
+
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
 
diff --git a/examples/Makefile b/examples/Makefile
index feff79784..b7e99a2f7 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -51,6 +51,9 @@ DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl
 ifeq ($(CONFIG_RTE_LIBRTE_LPM)$(CONFIG_RTE_LIBRTE_HASH),yy)
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH),y)
+DIRS-y += l3fwd-graph
+endif
 DIRS-y += link_status_interrupt
 DIRS-y += multi_process
 DIRS-y += ntb
diff --git a/examples/l3fwd-graph/Makefile b/examples/l3fwd-graph/Makefile
new file mode 100644
index 000000000..f3cf275ec
--- /dev/null
+++ b/examples/l3fwd-graph/Makefile
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# binary name
+APP = l3fwd-graph
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+# Build using pkg-config variables if possible
+ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)
+
+all: shared
+.PHONY: shared static
+shared: build/$(APP)-shared
+	ln -sf $(APP)-shared build/$(APP)
+static: build/$(APP)-static
+	ln -sf $(APP)-static build/$(APP)
+
+PKGCONF=pkg-config --define-prefix
+
+PC_FILE := $(shell $(PKGCONF) --path libdpdk)
+CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) -DALLOW_EXPERIMENTAL_API
+LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
+LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)
+
+build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
+
+build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
+
+build:
+	@mkdir -p $@
+
+.PHONY: clean
+clean:
+	rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
+	test -d build && rmdir -p build || true
+
+else # Build using legacy build system
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, detect a build directory, by looking for a path with a .config
+RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -O3 $(USER_FLAGS)
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+endif
diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
new file mode 100644
index 000000000..e0c6f42fa
--- /dev/null
+++ b/examples/l3fwd-graph/main.c
@@ -0,0 +1,525 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_branch_prediction.h>
+#include <rte_common.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_per_lcore.h>
+#include <rte_string_fns.h>
+#include <rte_vect.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
+
+/* Log type */
+#define RTE_LOGTYPE_L3FWD_GRAPH RTE_LOGTYPE_USER1
+
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 1024
+#define RTE_TEST_TX_DESC_DEFAULT 1024
+
+#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
+#define MAX_RX_QUEUE_PER_PORT 128
+
+#define MAX_RX_QUEUE_PER_LCORE 16
+
+#define MAX_LCORE_PARAMS 1024
+
+#define NB_SOCKETS 8
+
+/**< Ports set in promiscuous mode off by default. */
+static int promiscuous_on;
+
+static int numa_on = 1;	  /**< NUMA is enabled by default. */
+static int per_port_pool; /**< Use separate buffer pools per port; disabled */
+			  /**< by default */
+
+static volatile bool force_quit;
+
+/* Ethernet addresses of ports */
+static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+xmm_t val_eth[RTE_MAX_ETHPORTS];
+
+/* Mask of enabled ports */
+static uint32_t enabled_port_mask;
+
+struct lcore_rx_queue {
+	uint16_t port_id;
+	uint8_t queue_id;
+};
+
+/* Lcore conf */
+struct lcore_conf {
+	uint16_t n_rx_queue;
+	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+} __rte_cache_aligned;
+
+static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+
+struct lcore_params {
+	uint16_t port_id;
+	uint8_t queue_id;
+	uint8_t lcore_id;
+} __rte_cache_aligned;
+
+static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
+static struct lcore_params lcore_params_array_default[] = {
+	{0, 0, 2}, {0, 1, 2}, {0, 2, 2}, {1, 0, 2}, {1, 1, 2},
+	{1, 2, 2}, {2, 0, 2}, {3, 0, 3}, {3, 1, 3},
+};
+
+static struct lcore_params *lcore_params = lcore_params_array_default;
+static uint16_t nb_lcore_params = RTE_DIM(lcore_params_array_default);
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_RSS,
+		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+	},
+	.rx_adv_conf = {
+		.rss_conf = {
+				.rss_key = NULL,
+				.rss_hf = ETH_RSS_IP,
+		},
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static int
+check_lcore_params(void)
+{
+	uint8_t queue, lcore;
+	int socketid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		queue = lcore_params[i].queue_id;
+		if (queue >= MAX_RX_QUEUE_PER_PORT) {
+			printf("Invalid queue number: %hhu\n", queue);
+			return -1;
+		}
+		lcore = lcore_params[i].lcore_id;
+		if (!rte_lcore_is_enabled(lcore)) {
+			printf("Error: lcore %hhu is not enabled in lcore mask\n",
+			       lcore);
+			return -1;
+		}
+
+		if (lcore == rte_get_master_lcore()) {
+			printf("Error: lcore %u is master lcore\n", lcore);
+			return -1;
+		}
+		socketid = rte_lcore_to_socket_id(lcore);
+		if ((socketid != 0) && (numa_on == 0)) {
+			printf("Warning: lcore %hhu is on socket %d with numa off\n",
+			       lcore, socketid);
+		}
+	}
+
+	return 0;
+}
+
+static int
+check_port_config(void)
+{
+	uint16_t portid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		portid = lcore_params[i].port_id;
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("Port %u is not enabled in port mask\n", portid);
+			return -1;
+		}
+		if (!rte_eth_dev_is_valid_port(portid)) {
+			printf("Port %u is not present on the board\n", portid);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+init_lcore_rx_queues(void)
+{
+	uint16_t i, nb_rx_queue;
+	uint8_t lcore;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		lcore = lcore_params[i].lcore_id;
+		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
+			printf("Error: too many queues (%u) for lcore: %u\n",
+			       (unsigned int)nb_rx_queue + 1,
+			       (unsigned int)lcore);
+			return -1;
+		}
+
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+			lcore_params[i].port_id;
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+			lcore_params[i].queue_id;
+		lcore_conf[lcore].n_rx_queue++;
+	}
+
+	return 0;
+}
+
+/* Display usage */
+static void
+print_usage(const char *prgname)
+{
+	fprintf(stderr,
+		"%s [EAL options] --"
+		" -p PORTMASK"
+		" [-P]"
+		" --config (port,queue,lcore)[,(port,queue,lcore)]"
+		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
+		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
+		" [--no-numa]"
+		" [--per-port-pool]\n\n"
+
+		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
+		"  -P : Enable promiscuous mode\n"
+		"  --config (port,queue,lcore): Rx queue configuration\n"
+		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for "
+		"port X\n"
+		"  --enable-jumbo: Enable jumbo frames\n"
+		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"                 maximum packet length in decimal (64-9600)\n"
+		"  --no-numa: Disable numa awareness\n"
+		"  --per-port-pool: Use separate buffer pool per port\n\n",
+		prgname);
+}
+
+static int
+parse_max_pkt_len(const char *pktlen)
+{
+	unsigned long len;
+	char *end = NULL;
+
+	/* Parse decimal string */
+	len = strtoul(pktlen, &end, 10);
+	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (len == 0)
+		return -1;
+
+	return len;
+}
+
+static int
+parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_config(const char *q_arg)
+{
+	enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD };
+	unsigned long int_fld[_NUM_FLD];
+	const char *p, *p0 = q_arg;
+	char *str_fld[_NUM_FLD];
+	uint32_t size;
+	char s[256];
+	char *end;
+	int i;
+
+	nb_lcore_params = 0;
+
+	while ((p = strchr(p0, '(')) != NULL) {
+		++p;
+		p0 = strchr(p, ')');
+		if (p0 == NULL)
+			return -1;
+
+		size = p0 - p;
+		if (size >= sizeof(s))
+			return -1;
+
+		snprintf(s, sizeof(s), "%.*s", size, p);
+		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
+		    _NUM_FLD)
+			return -1;
+		for (i = 0; i < _NUM_FLD; i++) {
+			errno = 0;
+			int_fld[i] = strtoul(str_fld[i], &end, 0);
+			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
+				return -1;
+		}
+		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
+			printf("Exceeded max number of lcore params: %hu\n",
+			       nb_lcore_params);
+			return -1;
+		}
+		lcore_params_array[nb_lcore_params].port_id =
+			(uint8_t)int_fld[FLD_PORT];
+		lcore_params_array[nb_lcore_params].queue_id =
+			(uint8_t)int_fld[FLD_QUEUE];
+		lcore_params_array[nb_lcore_params].lcore_id =
+			(uint8_t)int_fld[FLD_LCORE];
+		++nb_lcore_params;
+	}
+	lcore_params = lcore_params_array;
+
+	return 0;
+}
+
+static void
+parse_eth_dest(const char *optarg)
+{
+	uint8_t c, *dest, peer_addr[6];
+	uint16_t portid;
+	char *port_end;
+
+	errno = 0;
+	portid = strtoul(optarg, &port_end, 10);
+	if (errno != 0 || port_end == optarg || *port_end++ != ',')
+		rte_exit(EXIT_FAILURE, "Invalid eth-dest: %s", optarg);
+	if (portid >= RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE,
+			 "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", portid,
+			 RTE_MAX_ETHPORTS);
+
+	if (cmdline_parse_etheraddr(NULL, port_end, &peer_addr,
+				    sizeof(peer_addr)) < 0)
+		rte_exit(EXIT_FAILURE, "Invalid ethernet address: %s\n",
+			 port_end);
+	dest = (uint8_t *)&dest_eth_addr[portid];
+	for (c = 0; c < 6; c++)
+		dest[c] = peer_addr[c];
+	*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+}
+
+#define MAX_JUMBO_PKT_LEN  9600
+#define MEMPOOL_CACHE_SIZE 256
+
+static const char short_options[] = "p:" /* portmask */
+				    "P"	 /* promiscuous */
+	;
+
+#define CMD_LINE_OPT_CONFIG	   "config"
+#define CMD_LINE_OPT_ETH_DEST	   "eth-dest"
+#define CMD_LINE_OPT_NO_NUMA	   "no-numa"
+#define CMD_LINE_OPT_ENABLE_JUMBO  "enable-jumbo"
+#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool"
+enum {
+	/* Long options mapped to a short option */
+
+	/* First long only option value must be >= 256, so that we won't
+	 * conflict with short options
+	 */
+	CMD_LINE_OPT_MIN_NUM = 256,
+	CMD_LINE_OPT_CONFIG_NUM,
+	CMD_LINE_OPT_ETH_DEST_NUM,
+	CMD_LINE_OPT_NO_NUMA_NUM,
+	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
+	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
+};
+
+static const struct option lgopts[] = {
+	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL},
+	{NULL, 0, 0, 0},
+};
+
+/*
+ * This expression is used to calculate the number of mbufs needed
+ * depending on user input, taking  into account memory for rx and
+ * tx hardware rings, cache per lcore and mtable per port per lcore.
+ * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum
+ * value of 8192
+ */
+#define NB_MBUF(nports)                                                        \
+	RTE_MAX((nports * nb_rx_queue * nb_rxd +                               \
+		 nports * nb_lcores * RTE_GRAPH_BURST_SIZE +                   \
+		 nports * n_tx_queue * nb_txd +                                \
+		 nb_lcores * MEMPOOL_CACHE_SIZE), 8192u)
+
+/* Parse the argument given in the command line of the application */
+static int
+parse_args(int argc, char **argv)
+{
+	char *prgname = argv[0];
+	int option_index;
+	char **argvopt;
+	int opt, ret;
+
+	argvopt = argv;
+
+	/* Error or normal output strings. */
+	while ((opt = getopt_long(argc, argvopt, short_options, lgopts,
+				  &option_index)) != EOF) {
+
+		switch (opt) {
+		/* Portmask */
+		case 'p':
+			enabled_port_mask = parse_portmask(optarg);
+			if (enabled_port_mask == 0) {
+				fprintf(stderr, "Invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case 'P':
+			promiscuous_on = 1;
+			break;
+
+		/* Long options */
+		case CMD_LINE_OPT_CONFIG_NUM:
+			ret = parse_config(optarg);
+			if (ret) {
+				fprintf(stderr, "Invalid config\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case CMD_LINE_OPT_ETH_DEST_NUM:
+			parse_eth_dest(optarg);
+			break;
+
+		case CMD_LINE_OPT_NO_NUMA_NUM:
+			numa_on = 0;
+			break;
+
+		case CMD_LINE_OPT_ENABLE_JUMBO_NUM: {
+			const struct option lenopts = {"max-pkt-len",
+						       required_argument, 0, 0};
+
+			port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME;
+			port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS;
+
+			/*
+			 * if no max-pkt-len set, use the default
+			 * value RTE_ETHER_MAX_LEN.
+			 */
+			if (getopt_long(argc, argvopt, "", &lenopts,
+					&option_index) == 0) {
+				ret = parse_max_pkt_len(optarg);
+				if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) {
+					fprintf(stderr, "Invalid maximum "
+							"packet length\n");
+					print_usage(prgname);
+					return -1;
+				}
+				port_conf.rxmode.max_rx_pkt_len = ret;
+			}
+			break;
+		}
+
+		case CMD_LINE_OPT_PARSE_PER_PORT_POOL:
+			printf("Per port buffer pool is enabled\n");
+			per_port_pool = 1;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind - 1] = prgname;
+	ret = optind - 1;
+	optind = 1; /* Reset getopt lib */
+
+	return ret;
+}
+
+static void
+signal_handler(int signum)
+{
+	if (signum == SIGINT || signum == SIGTERM) {
+		printf("\n\nSignal %d received, preparing to exit...\n",
+		       signum);
+		force_quit = true;
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	uint16_t portid;
+	int ret;
+
+	/* Init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+	argc -= ret;
+	argv += ret;
+
+	force_quit = false;
+	signal(SIGINT, signal_handler);
+	signal(SIGTERM, signal_handler);
+
+	/* Pre-init dst MACs for all ports to 02:00:00:00:00:xx */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		dest_eth_addr[portid] =
+			RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40);
+		*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+	}
+
+	/* Parse application arguments (after the EAL ones) */
+	ret = parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L3FWD_GRAPH parameters\n");
+
+	if (check_lcore_params() < 0)
+		rte_exit(EXIT_FAILURE, "check_lcore_params() failed\n");
+
+	ret = init_lcore_rx_queues();
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues() failed\n");
+
+	if (check_port_config() < 0)
+		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
+
+	printf("Bye...\n");
+
+	return ret;
+}
diff --git a/examples/l3fwd-graph/meson.build b/examples/l3fwd-graph/meson.build
new file mode 100644
index 000000000..a816bd890
--- /dev/null
+++ b/examples/l3fwd-graph/meson.build
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+deps += ['graph', 'eal', 'lpm', 'ethdev', 'node' ]
+sources = files(
+	'main.c'
+)
+allow_experimental_apis = true
diff --git a/examples/meson.build b/examples/meson.build
index 1f2b6f516..3b540012f 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -2,8 +2,10 @@
 # Copyright(c) 2017-2019 Intel Corporation
 
 driver_libs = []
+node_libs = []
 if get_option('default_library') == 'static'
 	driver_libs = dpdk_drivers
+	node_libs = dpdk_graph_nodes
 endif
 
 execinfo = cc.find_library('execinfo', required: false)
@@ -23,7 +25,7 @@ all_examples = [
 	'l2fwd', 'l2fwd-cat', 'l2fwd-event',
 	'l2fwd-crypto', 'l2fwd-jobstats',
 	'l2fwd-keepalive', 'l3fwd',
-	'l3fwd-acl', 'l3fwd-power',
+	'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph',
 	'link_status_interrupt',
 	'multi_process/client_server_mp/mp_client',
 	'multi_process/client_server_mp/mp_server',
@@ -99,7 +101,7 @@ foreach example: examples
 		endif
 		executable('dpdk-' + name, sources,
 			include_directories: includes,
-			link_whole: driver_libs,
+			link_whole: driver_libs + node_libs,
 			link_args: dpdk_extra_ldflags,
 			c_args: cflags,
 			dependencies: dep_objs)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 25/28] l3fwd-graph: add ethdev configuration changes
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (23 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 24/28] l3fwd-graph: add graph based l3fwd skeleton jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 26/28] l3fwd-graph: add graph config and main loop jerinj
                     ` (4 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add changes to ethdev port and queue configuration based
on command line parameters for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 350 +++++++++++++++++++++++++++++++++++-
 1 file changed, 349 insertions(+), 1 deletion(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index e0c6f42fa..47d2f2ecb 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -20,8 +20,10 @@
 
 #include <rte_branch_prediction.h>
 #include <rte_common.h>
+#include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
 #include <rte_per_lcore.h>
@@ -49,6 +51,10 @@
 
 #define NB_SOCKETS 8
 
+/* Static global variables used within this file. */
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
@@ -60,6 +66,7 @@ static volatile bool force_quit;
 
 /* Ethernet addresses of ports */
 static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+static struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
 xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 /* Mask of enabled ports */
@@ -110,6 +117,8 @@ static struct rte_eth_conf port_conf = {
 	},
 };
 
+static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
+
 static int
 check_lcore_params(void)
 {
@@ -165,6 +174,27 @@ check_port_config(void)
 	return 0;
 }
 
+static uint8_t
+get_port_n_rx_queues(const uint16_t port)
+{
+	int queue = -1;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		if (lcore_params[i].port_id == port) {
+			if (lcore_params[i].queue_id == queue + 1)
+				queue = lcore_params[i].queue_id;
+			else
+				rte_exit(EXIT_FAILURE,
+					 "Queue ids of the port %d must be"
+					 " in sequence and must start with 0\n",
+					 lcore_params[i].port_id);
+		}
+	}
+
+	return (uint8_t)(++queue);
+}
+
 static int
 init_lcore_rx_queues(void)
 {
@@ -470,6 +500,120 @@ parse_args(int argc, char **argv)
 	return ret;
 }
 
+static void
+print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
+{
+	char buf[RTE_ETHER_ADDR_FMT_SIZE];
+	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
+	printf("%s%s", name, buf);
+}
+
+static int
+init_mem(uint16_t portid, uint32_t nb_mbuf)
+{
+	uint32_t lcore_id;
+	int socketid;
+	char s[64];
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		if (numa_on)
+			socketid = rte_lcore_to_socket_id(lcore_id);
+		else
+			socketid = 0;
+
+		if (socketid >= NB_SOCKETS) {
+			rte_exit(EXIT_FAILURE,
+				 "Socket %d of lcore %u is out of range %d\n",
+				 socketid, lcore_id, NB_SOCKETS);
+		}
+
+		if (pktmbuf_pool[portid][socketid] == NULL) {
+			snprintf(s, sizeof(s), "mbuf_pool_%d:%d", portid,
+				 socketid);
+			/* Create a pool with priv size of a cacheline */
+			pktmbuf_pool[portid][socketid] =
+				rte_pktmbuf_pool_create(
+					s, nb_mbuf, MEMPOOL_CACHE_SIZE,
+					RTE_CACHE_LINE_SIZE,
+					RTE_MBUF_DEFAULT_BUF_SIZE, socketid);
+			if (pktmbuf_pool[portid][socketid] == NULL)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init mbuf pool on socket %d\n",
+					 socketid);
+			else
+				printf("Allocated mbuf pool on socket %d\n",
+				       socketid);
+		}
+	}
+
+	return 0;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90  /* 9s (90 * 100ms) in total */
+	uint8_t count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+	uint16_t portid;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		if (force_quit)
+			return;
+		all_ports_up = 1;
+		RTE_ETH_FOREACH_DEV(portid)
+		{
+			if (force_quit)
+				return;
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(portid, &link);
+			/* Print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status)
+					printf("Port%d Link Up. Speed %u Mbps "
+					       "-%s\n",
+					       portid, link.link_speed,
+					       (link.link_duplex ==
+						ETH_LINK_FULL_DUPLEX)
+						       ? ("full-duplex")
+						       : ("half-duplex\n"));
+				else
+					printf("Port %d Link Down\n", portid);
+				continue;
+			}
+			/* Clear all_ports_up flag if any link down */
+			if (link.link_status == ETH_LINK_DOWN) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* After finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* Set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("Done\n");
+		}
+	}
+}
+
 static void
 signal_handler(int signum)
 {
@@ -483,7 +627,14 @@ signal_handler(int signum)
 int
 main(int argc, char **argv)
 {
-	uint16_t portid;
+	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_eth_dev_info dev_info;
+	uint32_t n_tx_queue, nb_lcores;
+	struct rte_eth_txconf *txconf;
+	uint16_t queueid, portid;
+	struct lcore_conf *qconf;
+	uint32_t lcore_id;
+	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -519,6 +670,203 @@ main(int argc, char **argv)
 	if (check_port_config() < 0)
 		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
 
+	nb_ports = rte_eth_dev_count_avail();
+	nb_lcores = rte_lcore_count();
+
+	/* Initialize all ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		struct rte_eth_conf local_port_conf = port_conf;
+
+		/* Skip ports that are not enabled */
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("\nSkipping disabled port %d\n", portid);
+			continue;
+		}
+
+		/* Init port */
+		printf("Initializing port %d ... ", portid);
+		fflush(stdout);
+
+		nb_rx_queue = get_port_n_rx_queues(portid);
+		n_tx_queue = nb_lcores;
+		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
+			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
+		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
+		       nb_rx_queue, n_tx_queue);
+
+		rte_eth_dev_info_get(portid, &dev_info);
+		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+			local_port_conf.txmode.offloads |=
+				DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
+			dev_info.flow_type_rss_offloads;
+		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
+		    port_conf.rx_adv_conf.rss_conf.rss_hf) {
+			printf("Port %u modified RSS hash function based on "
+			       "hardware support,"
+			       "requested:%#" PRIx64 " configured:%#" PRIx64
+			       "\n",
+			       portid, port_conf.rx_adv_conf.rss_conf.rss_hf,
+			       local_port_conf.rx_adv_conf.rss_conf.rss_hf);
+		}
+
+		ret = rte_eth_dev_configure(portid, nb_rx_queue,
+					    n_tx_queue, &local_port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot configure device: err=%d, port=%d\n",
+				 ret, portid);
+
+		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
+						       &nb_txd);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot adjust number of descriptors: err=%d, "
+				 "port=%d\n",
+				 ret, portid);
+
+		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
+		print_ethaddr(" Address:", &ports_eth_addr[portid]);
+		printf(", ");
+		print_ethaddr(
+			"Destination:",
+			(const struct rte_ether_addr *)&dest_eth_addr[portid]);
+		printf(", ");
+
+		/*
+		 * prepare src MACs for each port.
+		 */
+		rte_ether_addr_copy(
+			&ports_eth_addr[portid],
+			(struct rte_ether_addr *)(val_eth + portid) + 1);
+
+		/* Init memory */
+		if (!per_port_pool) {
+			/* portid = 0; this is *not* signifying the first port,
+			 * rather, it signifies that portid is ignored.
+			 */
+			ret = init_mem(0, NB_MBUF(nb_ports));
+		} else {
+			ret = init_mem(portid, NB_MBUF(1));
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "init_mem() failed\n");
+
+		/* Init one TX queue per couple (lcore,port) */
+		queueid = 0;
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			qconf = &lcore_conf[lcore_id];
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
+			fflush(stdout);
+
+			txconf = &dev_info.default_txconf;
+			txconf->offloads = local_port_conf.txmode.offloads;
+			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+						     socketid, txconf);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_tx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+			queueid++;
+		}
+
+		printf("\n");
+	}
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		qconf = &lcore_conf[lcore_id];
+		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
+		fflush(stdout);
+		/* Init RX queues */
+		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+			struct rte_eth_rxconf rxq_conf;
+
+			portid = qconf->rx_queue_list[queue].port_id;
+			queueid = qconf->rx_queue_list[queue].queue_id;
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
+			fflush(stdout);
+
+			rte_eth_dev_info_get(portid, &dev_info);
+			rxq_conf = dev_info.default_rxconf;
+			rxq_conf.offloads = port_conf.rxmode.offloads;
+			if (!per_port_pool)
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf, pktmbuf_pool[0][socketid]);
+			else
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf,
+					pktmbuf_pool[portid][socketid]);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_rx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+
+		}
+	}
+
+	printf("\n");
+
+	/* Start ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* Start device */
+		ret = rte_eth_dev_start(portid);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "rte_eth_dev_start: err=%d, port=%d\n", ret,
+				 portid);
+
+		/*
+		 * If enabled, put device in promiscuous mode.
+		 * This allows IO forwarding mode to forward packets
+		 * to itself through 2 cross-connected  ports of the
+		 * target machine.
+		 */
+		if (promiscuous_on)
+			rte_eth_promiscuous_enable(portid);
+	}
+
+	printf("\n");
+
+	check_all_ports_link_status(enabled_port_mask);
+
+	/* Stop ports */
+	RTE_ETH_FOREACH_DEV(portid) {
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("Closing port %d...", portid);
+		rte_eth_dev_stop(portid);
+		rte_eth_dev_close(portid);
+		printf(" Done\n");
+	}
 	printf("Bye...\n");
 
 	return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 26/28] l3fwd-graph: add graph config and main loop
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (24 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 25/28] l3fwd-graph: add ethdev configuration changes jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 27/28] doc: add graph library programmer's guide guide jerinj
                     ` (3 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph creation, configuration logic and graph main loop.
This graph main loop is run on every slave lcore and calls
rte_graph_walk() to walk over lcore specific rte_graph.
Master core accumulates and prints graph walk stats of all the
lcore's graph's.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 242 +++++++++++++++++++++++++++++++++++-
 1 file changed, 240 insertions(+), 2 deletions(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index 47d2f2ecb..28db947b5 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -23,9 +23,13 @@
 #include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_graph_worker.h>
+#include <rte_launch.h>
 #include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
+#include <rte_node_eth_api.h>
+#include <rte_node_ip4_api.h>
 #include <rte_per_lcore.h>
 #include <rte_string_fns.h>
 #include <rte_vect.h>
@@ -75,12 +79,17 @@ static uint32_t enabled_port_mask;
 struct lcore_rx_queue {
 	uint16_t port_id;
 	uint8_t queue_id;
+	char node_name[RTE_NODE_NAMESIZE];
 };
 
 /* Lcore conf */
 struct lcore_conf {
 	uint16_t n_rx_queue;
 	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+
+	struct rte_graph *graph;
+	char name[RTE_GRAPH_NAMESIZE];
+	rte_graph_t graph_id;
 } __rte_cache_aligned;
 
 static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
@@ -119,6 +128,25 @@ static struct rte_eth_conf port_conf = {
 
 static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
 
+static struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS];
+
+struct ipv4_l3fwd_lpm_route {
+	uint32_t ip;
+	uint8_t depth;
+	uint8_t if_out;
+};
+
+#define IPV4_L3FWD_LPM_NUM_ROUTES                                              \
+	(sizeof(ipv4_l3fwd_lpm_route_array) /                                  \
+	 sizeof(ipv4_l3fwd_lpm_route_array[0]))
+/* 198.18.0.0/16 are set aside for RFC2544 benchmarking. */
+static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
+	{RTE_IPV4(198, 18, 0, 0), 24, 0}, {RTE_IPV4(198, 18, 1, 0), 24, 1},
+	{RTE_IPV4(198, 18, 2, 0), 24, 2}, {RTE_IPV4(198, 18, 3, 0), 24, 3},
+	{RTE_IPV4(198, 18, 4, 0), 24, 4}, {RTE_IPV4(198, 18, 5, 0), 24, 5},
+	{RTE_IPV4(198, 18, 6, 0), 24, 6}, {RTE_IPV4(198, 18, 7, 0), 24, 7},
+};
+
 static int
 check_lcore_params(void)
 {
@@ -624,17 +652,87 @@ signal_handler(int signum)
 	}
 }
 
+static void
+print_stats(void)
+{
+	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+	const char clr[] = {27, '[', '2', 'J', '\0'};
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker_*";
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL)
+		rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
+
+	while (!force_quit) {
+		/* Clear screen and move to top left */
+		printf("%s%s", clr, topLeft);
+		rte_graph_cluster_stats_get(stats, 0);
+		rte_delay_ms(1E3);
+	}
+
+	rte_graph_cluster_stats_destroy(stats);
+}
+
+/* Main processing loop */
+static int
+graph_main_loop(void *conf)
+{
+	struct lcore_conf *qconf;
+	struct rte_graph *graph;
+	uint32_t lcore_id;
+
+	RTE_SET_USED(conf);
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_conf[lcore_id];
+	graph = qconf->graph;
+
+	if (!graph) {
+		RTE_LOG(INFO, L3FWD_GRAPH, "Lcore %u has nothing to do\n",
+			lcore_id);
+		return 0;
+	}
+
+	RTE_LOG(INFO, L3FWD_GRAPH,
+		"Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+		qconf->name, graph);
+
+	while (likely(!force_quit))
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
 int
 main(int argc, char **argv)
 {
+	uint8_t rewrite_data[2 * sizeof(struct rte_ether_addr)];
+	static const char *node_patterns[] = {
+		"ip4*",
+		"ethdev_tx-*",
+		"pkt_drop",
+	};
 	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_graph_param graph_conf;
 	struct rte_eth_dev_info dev_info;
+	uint32_t nb_ports, nb_conf = 0;
 	uint32_t n_tx_queue, nb_lcores;
 	struct rte_eth_txconf *txconf;
-	uint16_t queueid, portid;
+	uint16_t queueid, portid, i;
 	struct lcore_conf *qconf;
+	uint16_t nb_graphs = 0;
+	uint16_t nb_patterns;
+	uint8_t rewrite_len;
 	uint32_t lcore_id;
-	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -783,6 +881,18 @@ main(int argc, char **argv)
 			queueid++;
 		}
 
+		/* Setup ethdev node config */
+		ethdev_conf[nb_conf].port_id = portid;
+		ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+		ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+		if (!per_port_pool)
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+
+		else
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+		ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+		nb_conf++;
 		printf("\n");
 	}
 
@@ -826,11 +936,26 @@ main(int argc, char **argv)
 					 "port=%d\n",
 					 ret, portid);
 
+			/* Add this queue node to its graph */
+			snprintf(qconf->rx_queue_list[queue].node_name,
+				 RTE_NODE_NAMESIZE, "ethdev_rx-%u-%u", portid,
+				 queueid);
+		}
+
+		/* Alloc a graph to this lcore only if source exists  */
+		if (qconf->n_rx_queue) {
+			qconf->graph_id = nb_graphs;
+			nb_graphs++;
 		}
 	}
 
 	printf("\n");
 
+	/* Ethdev node config, skip rx queue mapping */
+	ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+	if (ret)
+		rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", ret);
+
 	/* Start ports */
 	RTE_ETH_FOREACH_DEV(portid)
 	{
@@ -858,6 +983,119 @@ main(int argc, char **argv)
 
 	check_all_ports_link_status(enabled_port_mask);
 
+	/* Graph Initialization */
+	memset(&graph_conf, 0, sizeof(graph_conf));
+	graph_conf.node_patterns = node_patterns;
+	nb_patterns = RTE_DIM(node_patterns);
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		rte_graph_t graph_id;
+		rte_edge_t i;
+
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		qconf = &lcore_conf[lcore_id];
+
+		/* Skip graph creation if no source exists */
+		if (!qconf->n_rx_queue)
+			continue;
+
+		/* Add rx node patterns of this lcore */
+		for (i = 0; i < qconf->n_rx_queue; i++) {
+			graph_conf.node_patterns[nb_patterns + i] =
+				qconf->rx_queue_list[i].node_name;
+		}
+
+		graph_conf.nb_node_patterns = nb_patterns + i;
+		graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+		snprintf(qconf->name, sizeof(qconf->name), "worker_%u",
+			 lcore_id);
+
+		graph_id = rte_graph_create(qconf->name, &graph_conf);
+		if (graph_id != qconf->graph_id)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_create(): graph_id=%d not "
+				 " as expected for lcore %u(%u\n",
+				 graph_id, lcore_id, qconf->graph_id);
+
+		qconf->graph = rte_graph_lookup(qconf->name);
+		if (!qconf->graph)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_lookup(): graph %s not found\n",
+				 qconf->name);
+	}
+
+	memset(&rewrite_data, 0, sizeof(rewrite_data));
+	rewrite_len = sizeof(rewrite_data);
+
+	/* Add route to ip4 graph infra */
+	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+		char route_str[INET6_ADDRSTRLEN * 4];
+		char abuf[INET6_ADDRSTRLEN];
+		struct in_addr in;
+		uint32_t dst_port;
+		uint16_t next_hop;
+
+		/* Skip unused ports */
+		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
+		     enabled_port_mask) == 0)
+			continue;
+
+		dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+		next_hop = i;
+
+		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
+		snprintf(route_str, sizeof(route_str), "%s / %d (%d)",
+			 inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
+			 ipv4_l3fwd_lpm_route_array[i].depth,
+			 ipv4_l3fwd_lpm_route_array[i].if_out);
+
+		ret = rte_node_ip4_route_add(
+			ipv4_l3fwd_lpm_route_array[i].ip,
+			ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+			RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add ip4 route %s to graph\n",
+				 route_str);
+
+		memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+		/* Add next hop for a given destination */
+		ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+					       rewrite_len, dst_port);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add next hop %u for "
+				 "route %s\n",
+				 next_hop, route_str);
+
+		RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+			route_str, next_hop);
+	}
+
+	/* Launch per-lcore init on every slave lcore */
+	rte_eal_mp_remote_launch(graph_main_loop, NULL, SKIP_MASTER);
+
+	/* Accumulate and print stats on master until exit */
+	if (rte_graph_has_stats_feature())
+		print_stats();
+
+	/* Wait for slave cores to exit */
+	ret = 0;
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		ret = rte_eal_wait_lcore(lcore_id);
+		/* Destroy graph */
+		rte_graph_destroy(lcore_conf[lcore_id].name);
+		if (ret < 0) {
+			ret = -1;
+			break;
+		}
+	}
+
 	/* Stop ports */
 	RTE_ETH_FOREACH_DEV(portid) {
 		if ((enabled_port_mask & (1 << portid)) == 0)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 27/28] doc: add graph library programmer's guide guide
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (25 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 26/28] l3fwd-graph: add graph config and main loop jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 28/28] doc: add l3fwd graph application user guide jerinj
                     ` (2 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Adding programmer's guide for Graph library and the inbuilt nodes.
This patch also updates the release note for the new libraries.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   24 +
 6 files changed, 5532 insertions(+)
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg

diff --git a/doc/guides/prog_guide/graph_lib.rst b/doc/guides/prog_guide/graph_lib.rst
new file mode 100644
index 000000000..17101b10a
--- /dev/null
+++ b/doc/guides/prog_guide/graph_lib.rst
@@ -0,0 +1,397 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+Graph Library and Inbuilt Nodes
+===============================
+
+Graph architecture abstracts the data processing functions as a ``node`` and
+``links`` them together to create a complex ``graph`` to enable reusable/modular
+data processing functions.
+
+The graph library provides API to enable graph framework operations such as
+create, lookup, dump and destroy on graph and node operations such as clone,
+edge update, and edge shrink, etc. The API also allows to create the stats
+cluster to monitor per graph and per node stats.
+
+Features
+--------
+
+Features of the Graph library are:
+
+- Nodes as plugins.
+- Support for out of tree nodes.
+- Inbuilt nodes for packet processing.
+- Multi-process support.
+- Low overhead graph walk and node enqueue.
+- Low overhead statistics collection infrastructure.
+- Support to export the graph as a Graphviz dot file. See ``rte_graph_export()``.
+- Allow having another graph walk implementation in the future by segregating
+  the fast path(``rte_graph_worker.h``) and slow path code.
+
+Advantages of Graph architecture
+--------------------------------
+
+- Memory latency is the enemy for high-speed packet processing, moving the
+  similar packet processing code to a node will reduce the I cache and D
+  caches misses.
+- Exploits the probability that most packets will follow the same nodes in the
+  graph.
+- Allow SIMD instructions for packet processing of the node.-
+- The modular scheme allows having reusable nodes for the consumers.
+- The modular scheme allows us to abstract the vendor HW specific
+  optimizations as a node.
+
+Performance tuning parameters
+-----------------------------
+
+- Test with various burst size values (256, 128, 64, 32) using
+  CONFIG_RTE_GRAPH_BURST_SIZE config option.
+  The testing shows, on x86 and arm64 servers, The sweet spot is 256 burst
+  size. While on arm64 embedded SoCs, it is either 64 or 128.
+- Disable node statistics (using ``CONFIG_RTE_LIBRTE_GRAPH_STATS`` config option)
+  if not needed.
+- Use arm64 optimized memory copy for arm64 architecture by
+  selecting ``CONFIG_RTE_ARCH_ARM64_MEMCPY``.
+
+Programming model
+-----------------
+
+Anatomy of Node:
+~~~~~~~~~~~~~~~~
+
+.. _figure_anatomy_of_a_node:
+
+.. figure:: img/anatomy_of_a_node.*
+
+The :numref:`figure_anatomy_of_a_node` diagram depicts the anatomy of a node.
+
+The node is the basic building block of the graph framework.
+
+A node consists of:
+
+process():
+^^^^^^^^^^
+
+The callback function will be invoked by worker thread using
+``rte_graph_walk()`` function when there is data to be processed by the node.
+A graph node process the function using ``process()`` and enqueue to next
+downstream node using ``rte_node_enqueue*()`` function.
+
+Context memory:
+^^^^^^^^^^^^^^^
+
+It is memory allocated by the library to store the node-specific context
+information. This memory will be used by process(), init(), fini() callbacks.
+
+init():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_create()`` on when
+a node gets attached to a graph.
+
+fini():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_destroy()`` on when a
+node gets detached to a graph.
+
+Node name:
+^^^^^^^^^^
+
+It is the name of the node. When a node registers to graph library, the library
+gives the ID as ``rte_node_t`` type. Both ID or Name shall be used lookup the
+node. ``rte_node_from_name()``, ``rte_node_id_to_name()`` are the node
+lookup functions.
+
+nb_edges:
+^^^^^^^^^
+
+The number of downstream nodes connected to this node. The ``next_nodes[]``
+stores the downstream nodes objects. ``rte_node_edge_update()`` and
+``rte_node_edge_shrink()`` functions shall be used to update the ``next_node[]``
+objects. Consumers of the node APIs are free to update the ``next_node[]``
+objects till ``rte_graph_create()`` invoked.
+
+next_node[]:
+^^^^^^^^^^^^
+
+The dynamic array to store the downstream nodes connected to this node. Downstream
+node should not be current node itself or a source node.
+
+Source node:
+^^^^^^^^^^^^
+
+Source nodes are static nodes created using ``RTE_NODE_REGISTER`` by passing
+``flags`` as ``RTE_NODE_SOURCE_F``.
+While performing the graph walk, the ``process()`` function of all the source
+nodes will be called first. So that these nodes can be used as input nodes for a graph.
+
+Node creation and registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Node implementer creates the node by implementing ops and attributes of
+  ``struct rte_node_register``.
+
+* The library registers the node by invoking RTE_NODE_REGISTER on library load
+  using the constructor scheme. The constructor scheme used here to support multi-process.
+
+Link the Nodes to create the graph topology
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. _figure_link_the_nodes:
+
+.. figure:: img/link_the_nodes.*
+
+The :numref:`figure_link_the_nodes` diagram shows a graph topology after
+linking the N nodes.
+
+Once nodes are available to the program, Application or node public API
+functions can links them together to create a complex packet processing graph.
+
+There are multiple different types of strategies to link the nodes.
+
+Method (a):
+^^^^^^^^^^^
+Provide the ``next_nodes[]`` at the node registration time. See  ``struct rte_node_register::nb_edges``.
+This is a use case to address the static node scheme where one knows upfront the
+``next_nodes[]`` of the node.
+
+Method (b):
+^^^^^^^^^^^
+Use ``rte_node_edge_get()``, ``rte_node_edge_update()``, ``rte_node_edge_shrink()``
+to update the ``next_nodes[]`` links for the node runtime but before graph create.
+
+Method (c):
+^^^^^^^^^^^
+Use ``rte_node_clone()`` to clone a already existing node, created using RTE_NODE_REGISTER.
+When ``rte_node_clone()`` invoked, The library, would clone all the attributes
+of the node and creates a new one. The name for cloned node shall be
+``"parent_node_name-user_provided_name"``.
+
+This method enables the use case of Rx and Tx nodes where multiple of those nodes
+need to be cloned based on the number of CPU available in the system.
+The cloned nodes will be identical, except the ``"context memory"``.
+Context memory will have information of port, queue pair in case of Rx and Tx
+ethdev nodes.
+
+Create the graph object
+~~~~~~~~~~~~~~~~~~~~~~~
+Now that the nodes are linked, Its time to create a graph by including
+the required nodes. The application can provide a set of node patterns to
+form a graph object. The ``famish()`` API used underneath for the pattern
+matching to include the required nodes. After the graph create any changes to
+nodes or graph is not allowed.
+
+The ``rte_graph_create()`` API shall be used to create the graph.
+
+Example of a graph object creation:
+
+.. code-block:: console
+
+   {"ethdev_rx-0-0", ip4*, ethdev_tx-*"}
+
+In the above example, A graph object will be created with ethdev Rx
+node of port 0 and queue 0, all ipv4* nodes in the system,
+and ethdev tx node of all ports.
+
+Multicore graph processing
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+In the current graph library implementation, specifically,
+``rte_graph_walk()`` and ``rte_node_enqueue*`` fast path API functions
+are designed to work on single-core to have better performance.
+The fast path API works on graph object, So the multi-core graph
+processing strategy would be to create graph object PER WORKER.
+
+In fast path
+~~~~~~~~~~~~
+Typical fast-path code looks like below, where the application
+gets the fast-path graph object using ``rte_graph_lookup()``
+on the worker thread and run the ``rte_graph_walk()`` in a tight loop.
+
+.. code-block:: c
+
+    struct rte_graph *graph = rte_graph_lookup("worker0");
+
+    while (!done) {
+        rte_graph_walk(graph);
+    }
+
+Context update when graph walk in action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The fast-path object for the node is ``struct rte_node``.
+
+It may be possible that in slow-path or after the graph walk-in action,
+the user needs to update the context of the node hence access to
+``struct rte_node *`` memory.
+
+``rte_graph_foreach_node()``, ``rte_graph_node_get()``,
+``rte_graph_node_get_by_name()`` APIs can be used to to get the
+``struct rte_node*``. ``rte_graph_foreach_node()`` iterator function works on
+``struct rte_graph *`` fast-path graph object while others works on graph ID or name.
+
+Get the node statistics using graph cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The user may need to know the aggregate stats of the node across
+multiple graph objects. Especially the situation where each graph object bound
+to a worker thread.
+
+Introduced a graph cluster object for statistics.
+``rte_graph_cluster_stats_create()`` API shall be used for creating a
+graph cluster with multiple graph objects and ``rte_graph_cluster_stats_get()``
+to get the aggregate node statistics.
+
+An example statistics output from ``rte_graph_cluster_stats_get()``
+
+.. code-block:: diff
+
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+    |Node     |calls      |objs         |realloc_count  |objs/call  |objs/sec(10E6) |cycles/call|
+    +---------------------+-------------+---------------+-----------+---------------+-----------+
+    |node0    |12977424   |3322220544   |5              |256.000    |3047.151872    |20.0000    |
+    |node1    |12977653   |3322279168   |0              |256.000    |3047.210496    |17.0000    |
+    |node2    |12977696   |3322290176   |0              |256.000    |3047.221504    |17.0000    |
+    |node3    |12977734   |3322299904   |0              |256.000    |3047.231232    |17.0000    |
+    |node4    |12977784   |3322312704   |1              |256.000    |3047.243776    |17.0000    |
+    |node5    |12977825   |3322323200   |0              |256.000    |3047.254528    |17.0000    |
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+
+Node writing guidelines
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``process()`` function of a node is the fast-path function and that needs
+to be written carefully to achieve max performance.
+
+Broadly speaking, there are two different types of nodes.
+
+Static nodes
+~~~~~~~~~~~~
+The first kind of nodes are those that have a fixed ``next_nodes[]`` for the
+complete burst (like ethdev_rx, ethdev_tx) and it is simple to write.
+``process()`` function can move the obj burst to the next node either using
+``rte_node_next_stream_move()`` or using ``rte_node_next_stream_get()`` and
+``rte_node_next_stream_put()``.
+
+Intermediate nodes
+~~~~~~~~~~~~~~~~~~
+The second kind of such node is ``intermediate nodes`` that decide what is the
+``next_node[]`` to send to on a per-packet basis. In these nodes,
+
+* Firstly, there has to be the best possible packet processing logic.
+
+* Secondly, each packet needs to be queued to its next node.
+
+This can be done using ``rte_node_enqueue_[x1|x2|x4]()`` APIs if
+they are to single next or ``rte_node_enqueue_next()`` that takes array of nexts.
+
+In scenario where multiple intermediate nodes are present but most of the time
+each node using the same next node for all its packets, the cost of moving every
+pointer from current node's stream to next node's stream could be avoided.
+This is called home run and ``rte_node_next_stream_move()`` could be used to
+just move stream from the current node to the next node with least number of cycles.
+Since this can be avoided only in the case where all the packets are destined
+to the same next node, node implementation should be also having worst-case
+handling where every packet could be going to different next node.
+
+Example of intermediate node implementation with home run:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1. Start with speculation that next_node = node->ctx.
+This could be the next_node application used in the previous function call of this node.
+
+2. Get the next_node stream array with required space using
+``rte_node_next_stream_get(next_node, space)``.
+
+3. while n_left_from > 0 (i.e packets left to be sent) prefetch next pkt_set
+and process current pkt_set to find their next node
+
+4. if all the next nodes of the current pkt_set match speculated next node,
+just count them as successfully speculated(``last_spec``) till now and
+continue the loop without actually moving them to the next node. else if there is
+a mismatch, copy all the pkt_set pointers that were ``last_spec`` and move the
+current pkt_set to their respective next's nodes using ``rte_enqueue_next_x1()``.
+Also, one of the next_node can be updated as speculated next_node if it is more
+probable. Finally, reset ``last_spec`` to zero.
+
+5. if n_left_from != 0 then goto 3) to process remaining packets.
+
+6. if last_spec == nb_objs, All the objects passed were successfully speculated
+to single next node. So, the current stream can be moved to next node using
+``rte_node_next_stream_move(node, next_node)``.
+This is the ``home run`` where memcpy of buffer pointers to next node is avoided.
+
+7. Update the ``node->ctx`` with more probable next node.
+
+Graph object memory layout
+--------------------------
+.. _figure_graph_mem_layout:
+
+.. figure:: img/graph_mem_layout.*
+
+The :numref:`figure_graph_mem_layout` diagram shows ``rte_graph`` object memory
+layout. Understanding the memory layout helps to debug the graph library and
+improve the performance if needed.
+
+Graph object consists of a header, circular buffer to store the pending
+stream when walking over the graph, and variable-length memory to store
+the ``rte_node`` objects.
+
+The graph_nodes_mem_create() creates and populate this memory. The functions
+such as ``rte_graph_walk()`` and ``rte_node_enqueue_*`` use this memory
+to enable fastpath services.
+
+Inbuilt Nodes
+-------------
+
+DPDK provides a set of nodes for data processing. The following section
+details the documentation for the same.
+
+ethdev_rx
+~~~~~~~~~
+This node does ``rte_eth_rx_burst()`` into stream buffer passed to it
+(src node stream) and does ``rte_node_next_stream_move()`` only when
+there are packets received. Each ``rte_node`` works only on one Rx port and
+queue that it gets from node->ctx. For each (port X, rx_queue Y),
+a rte_node is cloned from  ethdev_rx_base_node as ``ethdev_rx-X-Y`` in
+``rte_node_eth_config()`` along with updating ``node->ctx``.
+Each graph needs to be associated  with a unique rte_node for a (port, rx_queue).
+
+ethdev_tx
+~~~~~~~~~
+This node does ``rte_eth_tx_burst()`` for a burst of objs received by it.
+It sends the burst to a fixed Tx Port and Queue information from
+node->ctx. For each (port X), this ``rte_node`` is cloned from
+ethdev_tx_node_base as "ethdev_tx-X" in ``rte_node_eth_config()``
+along with updating node->context.
+
+Since each graph doesn't need more than one Txq, per port, a Txq is assigned
+based on graph id to each rte_node instance. Each graph needs to be associated
+with a rte_node for each (port).
+
+pkt_drop
+~~~~~~~~
+This node frees all the objects passed to it considering them as
+``rte_mbufs`` that need to be freed.
+
+ip4_lookup
+~~~~~~~~~~
+This node is an intermediate node that does LPM lookup for the received
+ipv4 packets and the result determines each packets next node.
+
+On successful LPM lookup, the result contains the ``next_node`` id and
+``next-hop`` id with which the packet needs to be further processed.
+
+On LPM lookup failure, objects are redirected to pkt_drop node.
+``rte_node_ip4_route_add()`` is control path API to add ipv4 routes.
+To achieve home run, node use ``rte_node_stream_move()`` as mentioned in above
+sections.
+
+ip4_rewrite
+~~~~~~~~~~~
+This node gets packets from ``ip4_lookup`` node with next-hop id for each
+packet is embedded in ``rte_node_mbuf_priv1(mbuf)->nh``. This id is used
+to determine the L2 header to be written to the packet before sending
+the packet out to a particular ethdev_tx node.
+``rte_node_ip4_rewrite_add()`` is control path API to add next-hop info.
+
+null
+~~~~
+This node ignores the set of objects passed to it and reports that all are
+processed.
+
diff --git a/doc/guides/prog_guide/img/anatomy_of_a_node.svg b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
new file mode 100644
index 000000000..fa4b5b2d5
--- /dev/null
+++ b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
@@ -0,0 +1,1078 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg419"
+   sodipodi:docname="anatomy_of_a_node.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata425">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs423" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview421"
+     showgrid="false"
+     inkscape:zoom="2.406782"
+     inkscape:cx="470.64353"
+     inkscape:cy="284.06748"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg419" />
+  <clipPath
+     id="p.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path10" />
+  </clipPath>
+  <g
+     clip-path="url(#p.0)"
+     id="g417"
+     transform="matrix(1.0160138,0,0,1.0169275,-5.7394334,-5.6337913)">
+    <path
+       d="M 0,0 H 960 V 540 H 0 Z"
+       id="path13"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path15"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path17"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#741b47;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path19"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path21"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#c27ba0;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:4, 3, 1, 3" />
+    <path
+       d="M 540.8504,238.14069 V 415.12232"
+       id="path23"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="M 619.6588,238.14069 V 415.12232"
+       id="path25"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,238.63937 h 79.80579"
+       id="path27"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,273.8362 h 79.80579"
+       id="path29"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,309.03308 h 79.80579"
+       id="path31"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,344.22992 h 79.80579"
+       id="path33"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,379.42676 h 79.80579"
+       id="path35"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,414.62363 h 79.80579"
+       id="path37"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 559.7812,260.43936 v -9.54686 h 1.29687 l 5.01563,7.49998 v -7.49998 h 1.20312 v 9.54686 h -1.29687 l -5.01563,-7.49998 v 7.49998 z m 9.04706,-3.45312 q 0,-1.92186 1.07812,-2.84374 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57811 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28123 -0.59375,-1.93748 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98436 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03124 0.34375,-1.87499 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.
 79687,0.67188 v -3.42188 h 1.17188 v 9.54686 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42186 -0.54687,-2.07811 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,1.99999 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.74999 0.89063,-2.70311 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65623 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85936 -0.4375,-1.29686 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.43748 z m 9.89667,-0.57811 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.68
 75,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.29688 0.95313,0.89063 0.375,0.57812 0.59375,1.42187 0.21875,0.82813 0.21875,2.25 0,1.67186 -0.35938,2.70311 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.98438 -0.95313,-1.1875 -0.95313,-3.87498 z m 1.20313,0 q 0,2.34373 0.54687,3.12498 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.12498 0,-2.35937 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path39"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,295.63623 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.
 42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 H 598.128 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.1406
 2 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path41"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,330.83307 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29687 q -0.0156,-0.42187 0.14062,-0.8125 0.23438,-0.64062 0.76563,-1.26562 0.53125,-0.625
  1.53125,-1.45313 1.5625,-1.26562 2.10937,-2.01562 0.54688,-0.75 0.54688,-1.40625 0,-0.70313 -0.5,-1.17188 -0.5,-0.48437 -1.29688,-0.48437 -0.85937,0 -1.375,0.51562 -0.5,0.5 -0.5,1.39063 l -1.20312,-0.10938 q 0.125,-1.35937 0.92187,-2.0625 0.8125,-0.70312 2.17188,-0.70312 1.375,0 2.17187,0.76562 0.8125,0.75 0.8125,1.875 0,0.57813 -0.23437,1.14063 -0.23438,0.54687 -0.78125,1.15625 -0.54688,0.60937 -1.8125,1.67187 -1.04688,0.89063 -1.35938,1.21875 -0.29687,0.3125 -0.48437,0.625 z"
+       id="path43"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 577.7547,366.0299 v -1.32813 h 1.34375 v 1.32813 z m 3.703,0 v -1.32813 h 1.34375 v 1.32813 z"
+       id="path45"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,401.22678 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98438 q 0.75,-1.14063 2.1875,-1.14063 0.625,0 1.15625,0.21875 0.53125,0.218
 75 0.78125,0.59375 0.26562,0.35938 0.375,0.85938 0.0625,0.32812 0.0625,1.14062 v 4.25 h -1.17188 v -4.20312 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35938 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79687 v 3.78125 z"
+       id="path47"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path49"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path51"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 598.25494,126.88399 v -9.54688 h 1.29688 l 5.01562,7.5 v -7.5 h 1.20313 v 9.54688 h -1.29688 l -5.01562,-7.5 v 7.5 z m 9.047,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.4
 2188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z"
+       id="path53"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 596.41,142.88399 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 14.00012,-0.85938 q -0.65625,0.5625 -1.26562,0.79688 -0.59375,0.21875 -1.28125,0.21875 -1.14063,0 -1.75,-0.54688 -0.60938,-0.5625 -0.60938,-1.4375 0,-0.5 0.21875,-0.92187 0.23438,-0.42188 0.60938,-0.67188 0.375,-0.25 0.84375,-0.39062 0.34375,-0.0781 1.04687,-0.17188 1.42188,-0.17187 2.09375,-0.40625 0,-0.23437 0,-0.29687 0,-0.71875 -0.32812,-1.01563 -0.45313,-0.39062 -1.34375,-0.39062 -0.8125,0 -1.21875,0.29687 -0.39063,0.28125 -0.57813,1.01563 l -1.14062,-0.15625 q 0.15625,-0.73438 0.51562,-1.1875 0.35938,-0.45313 1.03125,-0.6875 0.67188,-0.25 1.5625,-0.25 0.89063,0 1.4375,0.20312 0.5625,0.20313 0.8125,0.53125 0.26563,0.3125 0.375,0.79688 0.0469,0.29687 0.0469,1.07812 v 1.5625 q 0,1.625 0.0781,2.0625 0.0781,0.4375 0.29688,0.82813 h -1.21875 q -0.1875,-0.35938 -0.23438,-0.85938 z m -0.0937,-2.60937 q -0.64062,0.26562 -1.92187,0.4375 -0.71875,0.10937
  -1.01563,0.25 -0.29687,0.125 -0.46875,0.375 -0.15625,0.25 -0.15625,0.54687 0,0.46875 0.34375,0.78125 0.35938,0.3125 1.04688,0.3125 0.67187,0 1.20312,-0.29687 0.53125,-0.29688 0.78125,-0.8125 0.1875,-0.39063 0.1875,-1.17188 z m 2.9906,3.46875 v -6.90625 h 1.04688 v 0.96875 q 0.32812,-0.51563 0.85937,-0.8125 0.54688,-0.3125 1.23438,-0.3125 0.78125,0 1.26562,0.3125 0.48438,0.3125 0.6875,0.89062 0.82813,-1.20312 2.14063,-1.20312 1.03125,0 1.57812,0.57812 0.5625,0.5625 0.5625,1.73438 v 4.75 h -1.17187 v -4.35938 q 0,-0.70312 -0.125,-1 -0.10938,-0.3125 -0.40625,-0.5 -0.29688,-0.1875 -0.70313,-0.1875 -0.71875,0 -1.20312,0.48438 -0.48438,0.48437 -0.48438,1.54687 v 4.01563 h -1.17187 v -4.48438 q 0,-0.78125 -0.29688,-1.17187 -0.28125,-0.39063 -0.92187,-0.39063 -0.5,0 -0.92188,0.26563 -0.42187,0.25 -0.60937,0.75 -0.1875,0.5 -0.1875,1.45312 v 3.57813 z m 15.83686,-2.21875 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0
 .89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z"
+       id="path55"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path57"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path59"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 186.03761,297.92676 v -11.48438 h 1.28125 v 1.07813 q 0.45312,-0.64063 1.01562,-0.95313 0.57813,-0.3125 1.39063,-0.3125 1.0625,0 1.875,0.54688 0.8125,0.54687 1.21875,1.54687 0.42187,0.98438 0.42187,2.17188 0,1.28125 -0.46875,2.29687 -0.45312,1.01563 -1.32812,1.5625 -0.85938,0.54688 -1.82813,0.54688 -0.70312,0 -1.26562,-0.29688 -0.54688,-0.29687 -0.90625,-0.75 v 4.04688 z m 1.26562,-7.29688 q 0,1.60938 0.64063,2.375 0.65625,0.76563 1.57812,0.76563 0.9375,0 1.60938,-0.79688 0.67187,-0.79687 0.67187,-2.45312 0,-1.59375 -0.65625,-2.375 -0.65625,-0.79688 -1.5625,-0.79688 -0.89062,0 -1.59375,0.84375 -0.6875,0.84375 -0.6875,2.4375 z m 7.61719,4.10938 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 4.8125,-4.15625 q 0,-2.29688 1.28125,-3.
 40625 1.07813,-0.92188 2.60938,-0.92188 1.71875,0 2.79687,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48437,2.51563 -0.48438,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73438,0 -2.8125,-1.10938 -1.07813,-1.125 -1.07813,-3.23437 z m 1.45313,0 q 0,1.59375 0.6875,2.39062 0.70312,0.79688 1.75,0.79688 1.04687,0 1.73437,-0.79688 0.70313,-0.79687 0.70313,-2.4375 0,-1.53125 -0.70313,-2.32812 -0.6875,-0.79688 -1.73437,-0.79688 -1.04688,0 -1.75,0.79688 -0.6875,0.78125 -0.6875,2.375 z m 13.38281,1.10937 1.39062,0.1875 q -0.23437,1.42188 -1.17187,2.23438 -0.92188,0.8125 -2.28125,0.8125 -1.70313,0 -2.75,-1.10938 -1.03125,-1.125 -1.03125,-3.20312 0,-1.34375 0.4375,-2.34375 0.45312,-1.01563 1.35937,-1.51563 0.92188,-0.5 1.98438,-0.5 1.35937,0 2.21875,0.6875 0.85937,0.67188 1.09375,1.9375 l -1.35938,0.20313 q -0.20312,-0.82813 -0.70312,-1.25 -0.48438,-0.42188 -1.1875,-0.42188 -1.0625,0 -1.73438,0.76563 -0.65625,0.75 -0.65625,2.40625 0,1.67187 0.64063,2.4375 0.64062,0.75 1.67187,0.
 75 0.82813,0 1.375,-0.5 0.5625,-0.51563 0.70313,-1.57813 z m 8.26562,0.375 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.27344,2.46875 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 
 0.25,-0.46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 8,0 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 0.25,-0.
 46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 11.25,5.85938 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375
 ,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path61"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path63"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path65"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 372.79996,224.29451 1.51562,0.375 q -0.46875,1.875 -1.71875,2.85937 -1.23437,0.98438 -3.01562,0.98438 -1.85938,0 -3.01563,-0.75 -1.15625,-0.76563 -1.76562,-2.1875 -0.60938,-1.4375 -0.60938,-3.07813 0,-1.79687 0.6875,-3.125 0.6875,-1.32812 1.9375,-2.01562 1.26563,-0.70313 2.78125,-0.70313 1.71875,0 2.89063,0.875 1.17187,0.875 1.64062,2.46875 l -1.5,0.34375 q -0.39062,-1.25 -1.15625,-1.8125 -0.75,-0.57812 -1.90625,-0.57812 -1.3125,0 -2.20312,0.64062 -0.89063,0.625 -1.25,1.70313 -0.35938,1.0625 -0.35938,2.1875 0,1.46875 0.42188,2.5625 0.4375,1.07812 1.32812,1.625 0.90625,0.53125 1.95313,0.53125 1.26562,0 2.14062,-0.73438 0.89063,-0.73437 1.20313,-2.17187 z m 2.67969,-0.14063 q 0,-2.29687 1.28125,-3.40625 1.07812,-0.92187 2.60937,-0.92187 1.71875,0 2.79688,1.125 1.09375,1.10937 1.09375,3.09375 0,1.59375 -0.48438,2.51562 -0.48437,0.92188 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10937 -1.07812,-1.125 -1.07812,-3.23438 z m 1.45312,0 q 0,1.59375 0.6875,2
 .39063 0.70313,0.79687 1.75,0.79687 1.04688,0 1.73438,-0.79687 0.70312,-0.79688 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32813 -0.6875,-0.79687 -1.73438,-0.79687 -1.04687,0 -1.75,0.79687 -0.6875,0.78125 -0.6875,2.375 z m 7.97656,4.15625 v -8.29687 h 1.26563 v 1.17187 q 0.90625,-1.35937 2.64062,-1.35937 0.75,0 1.375,0.26562 0.625,0.26563 0.9375,0.70313 0.3125,0.4375 0.4375,1.04687 0.0781,0.39063 0.0781,1.35938 v 5.10937 h -1.40625 v -5.04687 q 0,-0.85938 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57812 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 11.96094,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.59375,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 
 0.60937,-0.0625 z m 7.05469,-1.40625 1.45312,0.17187 q -0.34375,1.28125 -1.28125,1.98438 -0.92187,0.70312 -2.35937,0.70312 -1.82813,0 -2.89063,-1.125 -1.0625,-1.125 -1.0625,-3.14062 0,-2.09375 1.07813,-3.25 1.07812,-1.15625 2.79687,-1.15625 1.65625,0 2.70313,1.14062 1.0625,1.125 1.0625,3.17188 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76562,2.10937 0.70313,0.71875 1.73438,0.71875 0.78125,0 1.32812,-0.40625 0.54688,-0.40625 0.85938,-1.29687 z m -4.60938,-2.28125 h 4.625 q -0.0937,-1.04688 -0.53125,-1.5625 -0.67187,-0.8125 -1.73437,-0.8125 -0.96875,0 -1.64063,0.65625 -0.65625,0.64062 -0.71875,1.71875 z m 6.89844,4.95312 3.03125,-4.3125 -2.8125,-3.98437 h 1.76563 l 1.26562,1.9375 q 0.35938,0.5625 0.57813,0.9375 0.34375,-0.51563 0.64062,-0.92188 l 1.39063,-1.95312 h 1.6875 l -2.875,3.90625 3.09375,4.39062 h -1.73438 l -1.70312,-2.57812 -0.45313,-0.70313 -2.17187,3.28125 z m 12,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.593
 75,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z"
+       id="path67"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 364.5812,247.31013 V 235.857 h 2.28125 l 2.71875,8.10938 q 0.375,1.125 0.54688,1.6875 0.1875,-0.625 0.60937,-1.82813 l 2.73438,-7.96875 h 2.04687 v 11.45313 h -1.46875 v -9.59375 l -3.32812,9.59375 h -1.35938 l -3.3125,-9.75 v 9.75 z m 18.875,-2.67188 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.83593,4.95313 v -8.29688 h 1.25 v 1.15625 q 0.39063,-0.60937 1.03125,-0.96875 0.65625,-0.375 1.48438,-0.375 0.92187,0 1.51562,0.39063 0.59375,0.375 0
 .82813,1.0625 0.98437,-1.45313 2.5625,-1.45313 1.23437,0 1.89062,0.6875 0.67188,0.67188 0.67188,2.09375 v 5.70313 h -1.39063 v -5.23438 q 0,-0.84375 -0.14062,-1.20312 -0.14063,-0.375 -0.5,-0.59375 -0.35938,-0.23438 -0.84375,-0.23438 -0.875,0 -1.45313,0.57813 -0.57812,0.57812 -0.57812,1.85937 v 4.82813 h -1.40625 v -5.39063 q 0,-0.9375 -0.34375,-1.40625 -0.34375,-0.46875 -1.125,-0.46875 -0.59375,0 -1.09375,0.3125 -0.5,0.3125 -0.73438,0.92188 -0.21875,0.59375 -0.21875,1.71875 v 4.3125 z m 12.79688,-4.15625 q 0,-2.29688 1.28125,-3.40625 1.07812,-0.92188 2.60937,-0.92188 1.71875,0 2.79688,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48438,2.51563 -0.48437,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10938 -1.07812,-1.125 -1.07812,-3.23437 z m 1.45312,0 q 0,1.59375 0.6875,2.39062 0.70313,0.79688 1.75,0.79688 1.04688,0 1.73438,-0.79688 0.70312,-0.79687 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32812 -0.6875,-0.79688 -1.73438,-0.79688 -1.04687,0 -1.75,0.7968
 8 -0.6875,0.78125 -0.6875,2.375 z m 7.96094,4.15625 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 5.28125,3.20312 -0.15625,-1.32812 q 0.45313,0.125 0.79688,0.125 0.46875,0 0.75,-0.15625 0.28125,-0.15625 0.46875,-0.4375 0.125,-0.20313 0.42187,-1.04688 0.0469,-0.10937 0.125,-0.34375 l -3.14062,-8.3125 h 1.51562 l 1.71875,4.79688 q 0.34375,0.92187 0.60938,1.92187 0.23437,-0.96875 0.57812,-1.89062 l 1.76563,-4.82813 h 1.40625 l -3.15625,8.4375 q -0.5,1.375 -0.78125,1.89063 -0.375,0.6875 -0.85938,1.01562 -0.48437,0.32813 -1.15625,0.32813 -0.40625,0 -0.90625,-0.17188 z"
+       id="path69"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path71"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path73"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 330.8464,371.3732 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55469,0 v -8.29688 h 1.26562 v 1.17188 q 0.90625,-1.35938 2.64063,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17187,-1.28125 -0.15625,-0.4375 -0.57813,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64062,0.5625 -0.64062,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.61718,-1.26563 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23437 -0.42188,-0.25 -0.59375,-0.64063 -0.17188,-0.40625 -0.17188,-1.67187 v -4.76563 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60938 0.0625,0.78125 0.0781,0.17188 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z m
  4.07032,4.64063 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path75"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path77"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path79"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 418.20865,381.21695 v -7.20313 h -1.23438 v -1.09375 h 1.23438 v -0.89062 q 0,-0.82813 0.15625,-1.23438 0.20312,-0.54687 0.70312,-0.89062 0.51563,-0.34375 1.4375,-0.34375 0.59375,0 1.3125,0.14062 l -0.20312,1.23438 q -0.4375,-0.0781 -0.82813,-0.0781 -0.64062,0 -0.90625,0.28125 -0.26562,0.26563 -0.26562,1.01563 v 0.76562 h 1.60937 v 1.09375 h -1.60937 v 7.20313 z m 4.11719,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55468,0 v -8.29688 h 1.26563 v 1.17188 q 0.90625,-1.35938 2.64062,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.24219,3.375 q -1.17188,-1.4
 6875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path81"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path83"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path85"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path87"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path89"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path91"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path93"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 532.8373,210.97375 h 99.40155 v 27.46457 H 532.8373 Z"
+       id="path95"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 542.7123,232.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.14062,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 12.14685,-2.21875 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60
 938,1.4375 z m 5.7406,4.125 2.53125,-3.59375 -2.34375,-3.3125 h 1.46875 l 1.0625,1.60937 q 0.29688,0.46875 0.48438,0.78125 0.28125,-0.4375 0.51562,-0.76562 l 1.17188,-1.625 h 1.40625 l -2.39063,3.25 2.5625,3.65625 h -1.4375 l -1.42187,-2.14063 -0.375,-0.59375 -1.8125,2.73438 z m 10.00781,-1.04688 0.17188,1.03125 q -0.5,0.10938 -0.89063,0.10938 -0.64062,0 -1,-0.20313 -0.34375,-0.20312 -0.48437,-0.53125 -0.14063,-0.32812 -0.14063,-1.39062 v -3.96875 h -0.85937 v -0.90625 h 0.85937 v -1.71875 l 1.17188,-0.70313 v 2.42188 h 1.17187 v 0.90625 h -1.17187 v 4.04687 q 0,0.5 0.0469,0.64063 0.0625,0.14062 0.20313,0.23437 0.14062,0.0781 0.40625,0.0781 0.20312,0 0.51562,-0.0469 z m 0.0624,3.70313 v -0.85938 h 7.76563 v 0.85938 z m 8.4906,-2.65625 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.1406
 2,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188
  v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.36561,1.23438 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0.89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z m 6.0531,2.0625 1.15625,-0.1875 q 0.10937,0.70312 0.54687,1.07812 0.45313,0.35938 1.25,0.35938 0.8125,0 1.20
 313,-0.32813 0.39062,-0.32812 0.39062,-0.76562 0,-0.39063 -0.35937,-0.625 -0.23438,-0.15625 -1.1875,-0.39063 -1.29688,-0.32812 -1.79688,-0.5625 -0.48437,-0.25 -0.75,-0.65625 -0.25,-0.42187 -0.25,-0.9375 0,-0.45312 0.20313,-0.84375 0.21875,-0.40625 0.57812,-0.67187 0.28125,-0.1875 0.75,-0.32813 0.46875,-0.14062 1.01563,-0.14062 0.8125,0 1.42187,0.23437 0.60938,0.23438 0.90625,0.64063 0.29688,0.39062 0.40625,1.0625 l -1.14062,0.15625 q -0.0781,-0.53125 -0.45313,-0.82813 -0.375,-0.3125 -1.0625,-0.3125 -0.8125,0 -1.15625,0.26563 -0.34375,0.26562 -0.34375,0.625 0,0.23437 0.14063,0.42187 0.15625,0.1875 0.45312,0.3125 0.17188,0.0625 1.03125,0.29688 1.25,0.32812 1.73438,0.54687 0.5,0.20313 0.78125,0.60938 0.28125,0.40625 0.28125,1 0,0.59375 -0.34375,1.10937 -0.34375,0.51563 -1,0.79688 -0.64063,0.28125 -1.45313,0.28125 -1.34375,0 -2.04687,-0.5625 -0.70313,-0.5625 -0.90625,-1.65625 z m 7.16406,4.71875 v -12.20313 h 2.57812 v 0.96875 h -1.40625 v 10.25 h 1.40625 v 0.98438 z m 5.64044,0
  h -2.59375 v -0.98438 h 1.42188 v -10.25 h -1.42188 v -0.96875 h 2.59375 z"
+       id="path97"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789 z"
+       id="path99"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path101"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path103"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 445.60104,298.39108 h 99.40158 v 27.46457 h -99.40158 z"
+       id="path105"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 455.39792,318.91107 v -6.21875 h 0.9375 v 0.875 q 0.6875,-1.01563 1.98437,-1.01563 0.5625,0 1.03125,0.20313 0.48438,0.20312 0.71875,0.53125 0.23438,0.32812 0.32813,0.76562 0.0469,0.29688 0.0469,1.03125 v 3.82813 h -1.04687 v -3.78125 q 0,-0.65625 -0.125,-0.96875 -0.125,-0.3125 -0.4375,-0.5 -0.3125,-0.20313 -0.73438,-0.20313 -0.67187,0 -1.17187,0.4375 -0.48438,0.42188 -0.48438,1.60938 v 3.40625 z m 7.64258,0 h -0.98438 v -8.59375 h 1.0625 v 3.0625 q 0.67188,-0.82813 1.70313,-0.82813 0.57812,0 1.07812,0.23438 0.51563,0.21875 0.84375,0.64062 0.34375,0.42188 0.53125,1.01563 0.1875,0.59375 0.1875,1.26562 0,1.59375 -0.79687,2.46875 -0.79688,0.875 -1.89063,0.875 -1.10937,0 -1.73437,-0.92187 z m -0.0156,-3.15625 q 0,1.10937 0.3125,1.60937 0.5,0.8125 1.34375,0.8125 0.6875,0 1.1875,-0.59375 0.51563,-0.59375 0.51563,-1.79687 0,-1.21875 -0.48438,-1.79688 -0.48437,-0.57812 -1.17187,-0.57812 -0.6875,0 -1.20313,0.60937 -0.5,0.59375 -0.5,1.73438 z m 4.73633,5.54687 v -0.76562 h 
 7 v 0.76562 z m 11.9082,-4.39062 1.09375,0.125 q -0.25,0.95312 -0.95312,1.48437 -0.70313,0.53125 -1.78125,0.53125 -1.35938,0 -2.17188,-0.84375 -0.79687,-0.84375 -0.79687,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79687,0.84375 0.79687,2.39063 0,0.0937 0,0.28125 h -4.64062 q 0.0625,1.03125 0.57812,1.57812 0.51563,0.53125 1.29688,0.53125 0.57812,0 0.98437,-0.29687 0.42188,-0.3125 0.65625,-0.96875 z m -3.45312,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39063,-1.1875 -0.51562,-0.60937 -1.3125,-0.60937 -0.73437,0 -1.23437,0.48437 -0.48438,0.48438 -0.53125,1.3125 z m 9.9082,3.70313 v -0.78125 q -0.59375,0.92187 -1.73437,0.92187 -0.75,0 -1.375,-0.40625 -0.625,-0.42187 -0.96875,-1.15625 -0.34375,-0.73437 -0.34375,-1.6875 0,-0.92187 0.3125,-1.6875 0.3125,-0.76562 0.9375,-1.15625 0.625,-0.40625 1.39062,-0.40625 0.5625,0 1,0.23438 0.4375,0.23437 0.71875,0.60937 v -3.07812 h 1.04688 v 8.59375 z m -3.32812,-3.10938 q 0,1.20313 0.5,1.79688 0.5,0
 .57812 1.1875,0.57812 0.6875,0 1.17187,-0.5625 0.48438,-0.5625 0.48438,-1.71875 0,-1.28125 -0.5,-1.875 -0.48438,-0.59375 -1.20313,-0.59375 -0.70312,0 -1.17187,0.57813 -0.46875,0.5625 -0.46875,1.79687 z m 5.76758,3.625 1.03125,0.15625 q 0.0625,0.46875 0.35937,0.6875 0.39063,0.29688 1.0625,0.29688 0.73438,0 1.125,-0.29688 0.40625,-0.29687 0.54688,-0.8125 0.0937,-0.32812 0.0781,-1.35937 -0.6875,0.8125 -1.71875,0.8125 -1.28125,0 -1.98437,-0.92188 -0.70313,-0.9375 -0.70313,-2.21875 0,-0.89062 0.3125,-1.64062 0.32813,-0.76563 0.9375,-1.17188 0.60938,-0.40625 1.4375,-0.40625 1.10938,0 1.82813,0.89063 v -0.75 h 0.96875 v 5.375 q 0,1.45312 -0.29688,2.0625 -0.29687,0.60937 -0.9375,0.95312 -0.64062,0.35938 -1.57812,0.35938 -1.10938,0 -1.79688,-0.5 -0.6875,-0.5 -0.67187,-1.51563 z m 0.875,-3.73437 q 0,1.21875 0.48437,1.78125 0.48438,0.5625 1.21875,0.5625 0.73438,0 1.21875,-0.5625 0.5,-0.5625 0.5,-1.75 0,-1.14063 -0.51562,-1.71875 -0.5,-0.57813 -1.21875,-0.57813 -0.70313,0 -1.20313,0.578
 13 -0.48437,0.5625 -0.48437,1.6875 z m 10.25195,1.21875 1.09375,0.125 q -0.25,0.95312 -0.95313,1.48437 -0.70312,0.53125 -1.78125,0.53125 -1.35937,0 -2.17187,-0.84375 -0.79688,-0.84375 -0.79688,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79688,0.84375 0.79688,2.39063 0,0.0937 0,0.28125 h -4.64063 q 0.0625,1.03125 0.57813,1.57812 0.51562,0.53125 1.29687,0.53125 0.57813,0 0.98438,-0.29687 0.42187,-0.3125 0.65625,-0.96875 z m -3.45313,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39062,-1.1875 -0.51563,-0.60937 -1.3125,-0.60937 -0.73438,0 -1.23438,0.48437 -0.48437,0.48438 -0.53125,1.3125 z m 5.45508,1.84375 1.03125,-0.15625 q 0.0937,0.625 0.48438,0.95313 0.40625,0.32812 1.14062,0.32812 0.71875,0 1.0625,-0.28125 0.35938,-0.29687 0.35938,-0.70312 0,-0.35938 -0.3125,-0.5625 -0.21875,-0.14063 -1.07813,-0.35938 -1.15625,-0.29687 -1.60937,-0.5 -0.4375,-0.21875 -0.67188,-0.59375 -0.23437,-0.375 -0.23437,-0.84375 0,-0.40625 0.1875,-0.76562 0.1875,
 -0.35938 0.51562,-0.59375 0.25,-0.17188 0.67188,-0.29688 0.42187,-0.125 0.92187,-0.125 0.71875,0 1.26563,0.21875 0.5625,0.20313 0.82812,0.5625 0.26563,0.35938 0.35938,0.95313 l -1.03125,0.14062 q -0.0625,-0.46875 -0.40625,-0.73437 -0.32813,-0.28125 -0.95313,-0.28125 -0.71875,0 -1.03125,0.25 -0.3125,0.23437 -0.3125,0.5625 0,0.20312 0.125,0.35937 0.14063,0.17188 0.40625,0.28125 0.15625,0.0625 0.9375,0.26563 1.125,0.3125 1.5625,0.5 0.4375,0.1875 0.6875,0.54687 0.25,0.35938 0.25,0.90625 0,0.53125 -0.3125,1 -0.29687,0.45313 -0.875,0.71875 -0.57812,0.25 -1.3125,0.25 -1.21875,0 -1.85937,-0.5 -0.625,-0.51562 -0.79688,-1.5 z"
+       id="path107"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 89.115486,28.062992 H 289.55644 V 55.527557 H 89.115486 Z"
+       id="path109"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 98.084236,54.98299 5.125004,-13.359375 h 1.90625 l 5.46875,13.359375 h -2.01563 l -1.54687,-4.046875 h -5.59375 l -1.468754,4.046875 z m 3.859374,-5.484375 h 4.53125 l -1.40625,-3.703125 q -0.625,-1.6875 -0.9375,-2.765625 -0.26562,1.28125 -0.71875,2.546875 z m 9.84982,5.484375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07813,-1.59375 0.875,0 1.60937,0.3125 0.73438,0.3125 1.09375,0.828125 0.375,0.5 0.51563,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64063 v -5.890625 q 0,-1 -0.20312,-1.484375 -0.1875,-0.5 -0.67188,-0.796875 -0.48437,-0.296875 -1.14062,-0.296875 -1.04688,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 16.68821,-1.1875 q -0.92186,0.765625 -1.76561,1.09375 -0.82813,0.3125 -1.79688,0.3125 -1.59375,0 -2.45312,-0.78125 -0.85938,-0.78125 -0.85938,-1.984375 0,-0.71875 0.32813,-1.296875 0.32812,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45312,-0.25 1.98438,-0.234375 2.92187,-0.5625 0.0156,-
 0.34375 0.0156,-0.421875 0,-1 -0.46874,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70312,0.40625 -0.54688,0.40625 -0.8125,1.421875 l -1.60938,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45313,-0.984375 0.95312,-0.34375 2.1875,-0.34375 1.25,0 2.01561,0.296875 0.78125,0.28125 1.14063,0.734375 0.375,0.4375 0.51562,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10937,2.890625 0.10938,0.59375 0.40625,1.15625 h -1.70312 q -0.26563,-0.515625 -0.32813,-1.1875 z m -0.14062,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01562,0.140625 -1.4375,0.328125 -0.42187,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45313,0.4375 0.9375,0 1.67187,-0.40625 0.75,-0.421875 1.09374,-1.140625 0.26563,-0.5625 0.26563,-1.640625 z m 7.78197,3.390625 0.23437,1.453125 q -0.6875,0.140625 -1.23437,0.140625 -0.89063,0 -1.39063,-0.28125 -0.48437,-0.28125 -0.6875,-0.734375 -0.20312,-0.46875 -0.20312,-1.9375 V 46.57674
  h -1.20313 v -1.265625 h 1.20313 V 42.92049 l 1.625,-0.984375 v 3.375 h 1.65625 v 1.265625 h -1.65625 v 5.671875 q 0,0.6875 0.0781,0.890625 0.0937,0.203125 0.28125,0.328125 0.20313,0.109375 0.57813,0.109375 0.26562,0 0.71875,-0.0625 z m 0.9958,-3.375 q 0,-2.6875 1.48437,-3.96875 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.29759,4.84375 v -9.671875 h 1.46875 v 1.359375 q 0.45313,-0.71875 1.20313,-1.140625 0.76562,-0.4375 1.71875,-0.4375 1.07812,0 1.76562,0.453125 0.6875,0.4375 0.96875,1.234375 1.15625,-1.6875 2.98438,-1
 .6875 1.45312,0 2.21875,0.796875 0.78125,0.796875 0.78125,2.453125 v 6.640625 h -1.64063 v -6.09375 q 0,-0.984375 -0.15625,-1.40625 -0.15625,-0.4375 -0.57812,-0.703125 -0.42188,-0.265625 -0.98438,-0.265625 -1.01562,0 -1.6875,0.6875 -0.67187,0.671875 -0.67187,2.15625 v 5.625 h -1.64063 v -6.28125 q 0,-1.09375 -0.40625,-1.640625 -0.40625,-0.546875 -1.3125,-0.546875 -0.6875,0 -1.28125,0.359375 -0.59375,0.359375 -0.85937,1.0625 -0.25,0.703125 -0.25,2.03125 v 5.015625 z m 15.46268,3.71875 -0.1875,-1.53125 q 0.54687,0.140625 0.9375,0.140625 0.54687,0 0.875,-0.1875 0.32812,-0.171875 0.54687,-0.5 0.15625,-0.25 0.5,-1.21875 0.0469,-0.140625 0.14063,-0.40625 l -3.67188,-9.6875 h 1.76563 l 2.01562,5.59375 q 0.39063,1.078125 0.70313,2.25 0.28125,-1.125 0.67187,-2.203125 l 2.07813,-5.640625 h 1.64062 l -3.6875,9.828125 q -0.59375,1.609375 -0.92187,2.203125 -0.4375,0.8125 -1,1.1875 -0.5625,0.375 -1.34375,0.375 -0.48438,0 -1.0625,-0.203125 z m 13.98018,-8.5625 q 0,-2.6875 1.48437,-3.96875 
 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.68821,4.84375 v -8.40625 h -1.45312 v -1.265625 h 1.45312 v -1.03125 q 0,-0.96875 0.17188,-1.453125 0.23437,-0.640625 0.82812,-1.03125 0.59375,-0.390625 1.67188,-0.390625 0.6875,0 1.53125,0.15625 l -0.25,1.4375 q -0.5,-0.09375 -0.95313,-0.09375 -0.75,0 -1.0625,0.328125 -0.3125,0.3125 -0.3125,1.1875 v 0.890625 h 1.89063 v 1.265625 h -1.89063 v 8.40625 z m 16.28849,-1.1875 q -0.92188,0.765625 -1.76563,1.09375 -0.82812,0.3125 -1.79687,0.3125 -1.59375,0 -2.45313,-0.78125 -0.85937,
 -0.78125 -0.85937,-1.984375 0,-0.71875 0.32812,-1.296875 0.32813,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45313,-0.25 1.98437,-0.234375 2.92187,-0.5625 0.0156,-0.34375 0.0156,-0.421875 0,-1 -0.46875,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70313,0.40625 -0.54687,0.40625 -0.8125,1.421875 l -1.60937,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45312,-0.984375 0.95313,-0.34375 2.1875,-0.34375 1.25,0 2.01563,0.296875 0.78125,0.28125 1.14062,0.734375 0.375,0.4375 0.51563,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10938,2.890625 0.10937,0.59375 0.40625,1.15625 h -1.70313 q -0.26562,-0.515625 -0.32812,-1.1875 z m -0.14063,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01563,0.140625 -1.4375,0.328125 -0.42188,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45312,0.4375 0.9375,0 1.67188,-0.40625 0.75,-0.421875 1.09375,-1.140625 0.26562,-0.5625 0.26562
 ,-1.640625 z m 9.38715,4.859375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07812,-1.59375 0.875,0 1.60938,0.3125 0.73437,0.3125 1.09375,0.828125 0.375,0.5 0.51562,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64062 v -5.890625 q 0,-1 -0.20313,-1.484375 -0.1875,-0.5 -0.67187,-0.796875 -0.48438,-0.296875 -1.14063,-0.296875 -1.04687,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 9.76634,-4.84375 q 0,-2.6875 1.48438,-3.96875 1.25,-1.078125 3.04687,-1.078125 2,0 3.26563,1.3125 1.26562,1.296875 1.26562,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64062,1.65625 -1.0625,0.59375 -2.32813,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79688,2.796875 0.8125,0.921875 2.04687,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23437,0 -2.04687,0.921875 -0.79688,0.90625 -0.79688,2.765625 z m 15.56322,4.84375 v -1.2187
 5 q -0.90625,1.4375 -2.70313,1.4375 -1.15625,0 -2.125,-0.640625 -0.96875,-0.640625 -1.5,-1.78125 -0.53125,-1.140625 -0.53125,-2.625 0,-1.453125 0.48438,-2.625 0.48437,-1.1875 1.4375,-1.8125 0.96875,-0.625 2.17187,-0.625 0.875,0 1.54688,0.375 0.6875,0.359375 1.10937,0.953125 v -4.796875 h 1.64063 V 54.98299 Z m -5.17188,-4.828125 q 0,1.859375 0.78125,2.78125 0.78125,0.921875 1.84375,0.921875 1.07813,0 1.82813,-0.875 0.75,-0.890625 0.75,-2.6875 0,-1.984375 -0.76563,-2.90625 -0.76562,-0.9375 -1.89062,-0.9375 -1.07813,0 -1.8125,0.890625 -0.73438,0.890625 -0.73438,2.8125 z m 15.90697,1.71875 1.6875,0.203125 q -0.40625,1.484375 -1.48438,2.3125 -1.07812,0.8125 -2.76562,0.8125 -2.125,0 -3.375,-1.296875 -1.23438,-1.3125 -1.23438,-3.671875 0,-2.453125 1.25,-3.796875 1.26563,-1.34375 3.26563,-1.34375 1.9375,0 3.15625,1.328125 1.23437,1.3125 1.23437,3.703125 0,0.15625 0,0.4375 h -7.21875 q 0.0937,1.59375 0.90625,2.453125 0.8125,0.84375 2.01563,0.84375 0.90625,0 1.54687,-0.46875 0.64063,
 -0.484375 1.01563,-1.515625 z m -5.39063,-2.65625 h 5.40625 q -0.10937,-1.21875 -0.625,-1.828125 -0.78125,-0.953125 -2.03125,-0.953125 -1.125,0 -1.90625,0.765625 -0.76562,0.75 -0.84375,2.015625 z"
+       id="path111"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path113"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path115"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path117"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path119"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path121"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path123"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path125"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path127"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path129"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path131"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path133"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path135"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path137"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path139"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path141"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path143"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path145"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path147"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,107.32317 h 14.62836 v 5.59806 H 850.9376 Z"
+       id="path149"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path151"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path153"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path155"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,125.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path157"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path159"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path161"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path163"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path165"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path167"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path169"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path171"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path173"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path175"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path177"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path179"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path181"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path183"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path185"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path187"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path189"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path191"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path193"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path195"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path197"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path199"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path201"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path203"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path205"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path207"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path209"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path211"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path213"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,211.32317 h 14.62836 v 5.59807 H 850.9376 Z"
+       id="path215"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path217"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path219"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path221"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,229.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path223"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path225"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path227"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path229"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path231"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path233"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path235"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path237"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path239"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path241"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path243"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path245"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path247"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path249"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path251"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path253"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path255"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path257"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path259"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path261"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path263"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path265"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path267"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path269"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path271"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path273"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path275"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path277"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path279"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,307.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path281"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path283"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path285"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path287"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,325.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path289"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path291"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path293"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path295"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path297"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path299"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path301"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path303"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path305"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path307"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path309"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path311"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path313"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path315"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path317"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path319"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path321"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path323"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path325"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path327"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path329"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path331"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path333"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path335"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path337"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path339"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path341"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path343"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path345"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,403.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path347"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path349"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path351"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path353"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,421.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path355"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path357"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path359"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path361"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path363"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path365"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path367"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path369"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path371"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path373"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path375"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90552,-71.85828 20.47638,-35.92914 40.95276,-71.85827 81.90552,-71.85827"
+       id="path377"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90546,-71.85826 10.23822,-17.96457 20.47638,-35.92915 33.27411,-49.40257 6.39886,-6.73671 13.43762,-12.35065 21.43622,-16.2804 3.99933,-1.96487 8.23858,-3.5087 12.75781,-4.56131 2.25958,-0.52631 4.58911,-0.92981 6.99371,-1.20174 1.20227,-0.13596 2.42334,-0.23902 3.66376,-0.3081 l 0.35388,-0.0172"
+       id="path379"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1456,117.62166 -1.0957,1.15275 3.06024,-1.20262 -3.11731,-1.04582 z"
+       id="path381"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74017,-33.62204 C 722.18577,242.81102 742.87083,226 784.24088,226"
+       id="path383"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74011,-33.62204 10.34253,-8.4055 20.68506,-16.81102 33.61322,-23.11514 6.46405,-3.15207 13.57452,-5.7788 21.6546,-7.61751 4.0401,-0.91934 8.32251,-1.64169 12.88776,-2.1342 2.28265,-0.24626 4.63592,-0.43506 7.06506,-0.5623 1.21448,-0.0636 2.448,-0.11184 3.70105,-0.14415 l 0.39166,-0.009"
+       id="path385"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 780.814,226.03992 -1.11139,1.1376 3.07648,-1.16049 -3.10266,-1.08851 z"
+       id="path387"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 19.86224,3.52755 39.72443,7.05511 79.44885,7.05511"
+       id="path389"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 9.93109,1.76376 19.86218,3.52755 32.27606,4.85037 6.20697,0.66144 13.03455,1.21261 20.79322,1.59842 3.87939,0.19293 7.99151,0.34451 12.37512,0.44784 2.19183,0.0517 4.45147,0.0913 6.78399,0.11798 1.1662,0.0133 2.35065,0.0235 3.55384,0.0303 l 0.2395,9.7e-4"
+       id="path391"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 774.23114,347.34512 -1.12762,1.12155 3.09283,-1.11627 -3.08673,-1.1329 z"
+       id="path393"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90552,22.1575 20.47638,11.07874 40.95276,22.15747 81.90552,22.15747"
+       id="path395"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90546,22.1575 10.23822,5.53937 20.47638,11.07874 33.27411,15.23328 6.39886,2.07724 13.43762,3.80829 21.43622,5.02005 3.99933,0.60583 8.23858,1.08188 12.75781,1.40649 2.25958,0.16226 4.58911,0.28668 6.99371,0.37052 1.20227,0.0419 2.42334,0.0737 3.66376,0.095 l 0.35291,0.005"
+       id="path397"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1446,441.53223 -1.1333,1.11575 3.09845,-1.10037 -3.08087,-1.14874 z"
+       id="path399"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 780.8373,58.973755 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path401"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="M 811.0304,80.77375 V 73.8675 h 1.0625 v 0.984375 q 0.75,-1.140625 2.1875,-1.140625 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.359375 0.375,0.859375 0.0625,0.328125 0.0625,1.140625 v 4.25 h -1.17188 v -4.203125 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.359375 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.796875 v 3.78125 z m 6.97498,-3.453125 q 0,-1.921875 1.07812,-2.84375 0.89063,-0.765625 2.17188,-0.765625 1.42187,0 2.32812,0.9375 0.90625,0.921875 0.90625,2.578125 0,1.328125 -0.40625,2.09375 -0.39062,0.765625 -1.15625,1.1875 -0.76562,0.421875 -1.67187,0.421875 -1.45313,0 -2.35938,-0.921875 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.328125 0.57813,1.984375 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.671875 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.984375 z m 
 11.13123,3.453125 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.453125 -0.6875,-0.453125 -1.07812,-1.265625 -0.375,-0.828125 -0.375,-1.890625 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.453125 1.54687,-0.453125 0.625,0 1.10938,0.265625 0.5,0.25 0.79687,0.671875 v -3.421875 h 1.17188 v 9.546875 z m -3.70313,-3.453125 q 0,1.328125 0.5625,1.984375 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.421875 -0.54687,-2.078125 Q 829.2616,74.68 828.46473,74.68 q -0.78125,0 -1.3125,0.640625 -0.51563,0.625 -0.51563,2 z m 11.3656,1.234375 1.20313,0.140625 q -0.28125,1.0625 -1.0625,1.65625 Q 837.3772,80.93 836.17408,80.93 q -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.609375 0,-1.75 0.89063,-2.703125 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.109375 0,0.3125 h -5.15625 q 0.0625,1.140625 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1
 .10937,-0.328125 0.45313,-0.34375 0.71875,-1.078125 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.859375 -0.4375,-1.296875 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.546875 -0.54688,0.53125 -0.60938,1.4375 z m 9.89667,-0.578125 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.6875,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.296875 0.95313,0.890625 0.375,0.578125 0.59375,1.421875 0.21875,0.828125 0.21875,2.25 0,1.671875 -0.35938,2.703125 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.984375 -0.95313,-1.1875 -0.95313,-3.875 z m 1.20313,0 q 0,2.34375 0.54687,3.125 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.125 0,-2.359375 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path403"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,162.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path405"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,184.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 h -1.17188 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.14062 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path407"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,258.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path409"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,280.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29688 q -0.0156,-0.42188 0.14063,-0.8125 0.23437,-0.64063 0.76562,-1.26563 0.53125,-0.625 1.53125,-1.45312 1.5625,-1.26563 2.10938,-2.01563 0.54687,-0.75 0.54687,-1.40625 0,-0.70312 -0.5,-1.17187 -0.5,-0.48438 -1.29687,-0.48438 -0.85938,0 -1.375,0.51563 -0.5,0.5 -0.5,1.39062 l -1.20313,-0.10937 q 0.125,-1.35938 0.92188,-2.0625 0.8125,-0.70313 2.17187,-0.70313 1.375,0 2.17188,0.76563 0.8125,0.75 0.8125,1.875 0,0.57812 -0.23438,1.14062 -0.23437,0.54688 -0.78125,1.15625 -0.54687,0.60938 -1.8125,1.67188 -1.04687,0.89062 -1.35937,1.21875 -0.29688,0.3125 -0.48438,0.625 z"
+       id="path411"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,354.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path413"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,376.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z"
+       id="path415"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/graph_mem_layout.svg b/doc/guides/prog_guide/img/graph_mem_layout.svg
new file mode 100644
index 000000000..1d41729c9
--- /dev/null
+++ b/doc/guides/prog_guide/img/graph_mem_layout.svg
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg428"
+   sodipodi:docname="Graph_mem_layout.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata434">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs432" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview430"
+     showgrid="false"
+     inkscape:zoom="3.4037037"
+     inkscape:cx="505.84248"
+     inkscape:cy="270.46053"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g426" />
+  <clipPath
+     id="g81c521b992_0_3.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path223" />
+  </clipPath>
+  <g
+     clip-path="url(#g81c521b992_0_3.0)"
+     id="g426">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path226" />
+    <path
+       fill="#ffffff"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path228" />
+    <path
+       stroke="#741b47"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path230" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m143.0105 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path232" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m271.45407 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path234" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 100.52126l129.44095 0"
+       fill-rule="nonzero"
+       id="path236" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 151.71811l129.44095 0"
+       fill-rule="nonzero"
+       id="path238" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 204.91496l129.44095 0"
+       fill-rule="nonzero"
+       id="path240" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 240.11182l129.44095 0"
+       fill-rule="nonzero"
+       id="path242" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 291.30865l129.44095 0"
+       fill-rule="nonzero"
+       id="path244" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 342.50552l129.44095 0"
+       fill-rule="nonzero"
+       id="path246" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 393.70236l129.44095 0"
+       fill-rule="nonzero"
+       id="path248" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 444.8992l129.44095 0"
+       fill-rule="nonzero"
+       id="path250" />
+    <path
+       fill="#000000"
+       d="m172.35416 118.58688l0 -1.125l4.03125 -0.015625l0 3.546875q-0.921875 0.75 -1.921875 1.125q-0.984375 0.359375 -2.03125 0.359375q-1.40625 0 -2.5625 -0.59375q-1.140625 -0.609375 -1.734375 -1.734375q-0.578125 -1.140625 -0.578125 -2.546875q0 -1.40625 0.578125 -2.609375q0.59375 -1.203125 1.6875 -1.78125q1.09375 -0.59375 2.515625 -0.59375q1.03125 0 1.859375 0.34375q0.84375 0.328125 1.3125 0.9375q0.484375 0.59375 0.734375 1.546875l-1.140625 0.3125q-0.21875 -0.71875 -0.53125 -1.140625q-0.3125 -0.421875 -0.90625 -0.671875q-0.59375 -0.25 -1.3125 -0.25q-0.875 0 -1.515625 0.265625q-0.625 0.265625 -1.015625 0.703125q-0.375 0.421875 -0.59375 0.9375q-0.359375 0.875 -0.359375 1.921875q0 1.265625 0.4375 2.125q0.4375 0.859375 1.265625 1.28125q0.84375 0.421875 1.796875 0.421875q0.8125 0 1.59375 -0.3125q0.78125 -0.328125 1.1875 -0.6875l0 -1.765625l-2.796875 0zm5.726425 3.734375l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.593
 75 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q
 -0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.7031
 25 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm11.302963 0l0 -9.546875l1.265625 0l0 3.921875l4.953125 0l0 -3.921875l1.265625 0l0 9.546875l-1.265625 0l0 -4.5l-4.953125 0l0 4.5l-1.265625 0zm14.172028 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.718
 75 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.3
 59375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm7.4749756 3.46875l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234
 375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path252" />
+    <path
+       fill="#000000"
+       d="m161.10791 135.96188l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.631226 -2.03125l1.1875 0.109375q0 0.40625 0.109375 0.609375q0.109375 0.203125 0.34375 0.3125q0.328125 0.140625
  0.828125 0.140625q1.0625 0 1.53125 -0.546875q0.3125 -0.375 0.578125 -1.625l0.109375 -0.5625q-0.921875 0.9375 -1.953125 0.9375q-1.046875 0 -1.75 -0.765625q-0.703125 -0.78125 -0.703125 -2.1875q0 -1.171875 0.546875 -2.140625q0.5625 -0.984375 1.328125 -1.46875q0.765625 -0.5 1.578125 -0.5q1.359375 0 2.09375 1.28125l0.234375 -1.125l1.078125 0l-1.390625 6.671875q-0.21875 1.09375 -0.59375 1.703125q-0.375 0.625 -1.03125 0.953125q-0.65625 0.34375 -1.53125 0.34375q-0.828125 0 -1.4375 -0.21875q-0.59375 -0.203125 -0.890625 -0.625q-0.296875 -0.40625 -0.296875 -0.953125q0 -0.15625 0.03125 -0.34375zm1.46875 -3.6875q0 0.71875 0.140625 1.078125q0.203125 0.5 0.5625 0.765625q0.359375 0.25 0.796875 0.25q0.578125 0 1.140625 -0.40625q0.578125 -0.40625 0.9375 -1.25q0.359375 -0.859375 0.359375 -1.625q0 -0.859375 -0.484375 -1.359375q-0.46875 -0.515625 -1.15625 -0.515625q-0.4375 0 -0.84375 0.234375q-0.390625 0.234375 -0.75 0.703125q-0.34375 0.46875 -0.53125 1.140625q-0.171875 0.65625 -0.171875 0.9843
 75zm6.0062256 3.0625l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.110245 -0.859375q-0.625 0.53125 -1.1875 0.78125q-0.5625 0.234375 -1.203125 0.234375q-0.96875 0 -1.5625 -0.5625q-0.578125 -0.5625 -0.578125 -1.4375q0 -0.578125 0.265625 -1.015625q0.265625 -0.453125 0.703125 -0.71875q0.4375 -0.28125 1.0625 -0.390625q0.40625 -0.078125 1.515625 -0.125q1.109375 -0.046875 1.59375 -0.234375q0.125 -0.484375 0.125 -0.8125q0 -0.40625 -0.296875 -0.640625q-0.40625 -0.328125 -1.1875 -0.328125q-0.75 0 -1.21875 0.328125q-0.46875 0.328125 -0.6875 0.9375l-1.1875 -0.109375q0.359375 -1.015625 1.140625 -1.5625q0.796875 -0.546875 2.0 -0.546875q1.28125 0 2.03125 0.609375q0.578125 0.453125 0.578125 1.1875q0 0.546875 -0.15625 1.28125l-0.390625 1.71875q-0.1875 0.8
 125 -0.1875 1.328125q0 0.328125 0.15625 0.9375l-1.203125 0q-0.09375 -0.34375 -0.125 -0.859375zm0.421875 -2.640625q-0.234375 0.09375 -0.53125 0.15625q-0.28125 0.046875 -0.9375 0.109375q-1.03125 0.078125 -1.453125 0.21875q-0.421875 0.140625 -0.640625 0.453125q-0.21875 0.296875 -0.21875 0.671875q0 0.5 0.34375 0.828125q0.34375 0.3125 0.984375 0.3125q0.578125 0 1.109375 -0.3125q0.546875 -0.3125 0.859375 -0.859375q0.3125 -0.5625 0.484375 -1.578125zm1.7406006 6.15625l2.0 -9.5625l1.09375 0l-0.203125 0.953125q0.59375 -0.625 1.078125 -0.859375q0.484375 -0.25 1.015625 -0.25q0.984375 0 1.625 0.71875q0.65625 0.71875 0.65625 2.0625q0 1.078125 -0.359375 1.96875q-0.34375 0.875 -0.875 1.421875q-0.515625 0.546875 -1.046875 0.796875q-0.53125 0.25 -1.09375 0.25q-1.25 0 -1.921875 -1.265625l-0.78125 3.765625l-1.1875 0zm2.328125 -5.484375q0 0.78125 0.109375 1.078125q0.171875 0.421875 0.53125 0.6875q0.375 0.25 0.875 0.25q1.015625 0 1.640625 -1.140625q0.625 -1.140625 0.625 -2.328125q0 -0.875 -0.4218
 75 -1.359375q-0.421875 -0.484375 -1.046875 -0.484375q-0.453125 0 -0.84375 0.25q-0.375 0.234375 -0.703125 0.703125q-0.328125 0.46875 -0.546875 1.15625q-0.21875 0.6875 -0.21875 1.1875zm5.6624756 2.828125l2.0 -9.546875l1.171875 0l-0.765625 3.671875q0.65625 -0.640625 1.21875 -0.90625q0.578125 -0.28125 1.171875 -0.28125q0.859375 0 1.328125 0.453125q0.484375 0.453125 0.484375 1.1875q0 0.359375 -0.203125 1.34375l-0.859375 4.078125l-1.171875 0l0.875 -4.1875q0.1875 -0.90625 0.1875 -1.140625q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.671875 -0.21875q-0.640625 0 -1.21875 0.34375q-0.578125 0.328125 -0.90625 0.90625q-0.328125 0.578125 -0.609375 1.875l-0.609375 2.96875l-1.1875 0z"
+       fill-rule="nonzero"
+       id="path254" />
+    <path
+       fill="#000000"
+       d="m189.80461 173.51811l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875
  -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.968
 75 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path256" />
+    <path
+       fill="#000000"
+       d="m160.76096 182.86186q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125
  -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.93988 -1.703125q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0625 0.046875 -0.125 0.046875l-2.359375 0l-1.46875 7.296875q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.26
 5625 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 -0.015625 -0.109375l1.46875 -7.296875l-2.359375 0q-0.09375 0 -0.125 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.09375 0.046875 -0.171875q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.078125 0.09375 -0.109375q0.046875 -0.046875 0.109375 -0.046875l5.859375 0q0.078125 0 0.109375 0.0625q0.03125 0.0625 0.03125 0.171875zm5.838608 -0.015625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0468
 75 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm5.4453735 9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625
  -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm9.278656 -9.234375q0 0.046875 -0.015625 0.109375q-0.015625 0.0625 -0.03125 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.078125 0.109375q-0.03125 0.03125 -0.078125 0.03125q-0.09375 0 -0.265625 -0.109375q-0.171875 -0.125 -0.453125 -0.265625q-0.265625 -0.15625 -0.671875 -0.265625q-0.390625 -0.125 -0.9375 -0.125q-0.578125 0 -1.09375 0.171875q-0.5 0.171875 -0.921875 0.484375q-0.421875 0.296875 -0.75 0.703125q-0.328125 0.40625 -0.5625 0.90625q-0.234375 0.484375 -0.359375 1.015625q-0.109375 0.53125 -0.109375 1.078125q0 0.5625 0.15625 1.015625q0.171875 0.4375 0.484375 0.734375q0.3125 0.296875 0.75 0.453125q0.4375 0.15625 0.984375 0.15625q0.40625 0 0.84375 -0.09375q0.453125 -0.09375 0.828125 -0.296875l0.484375 -2.4375l-1.953125 0q-0.078125 0 -0.125 -0.046875q-0.03125 -0.0625 -
 0.03125 -0.171875q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.078125 0.046875 -0.15625q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.0625 0.09375 -0.09375q0.046875 -0.046875 0.109375 -0.046875l2.671875 0q0.1875 0 0.265625 0.125q0.078125 0.125 0.03125 0.328125l-0.640625 3.25q-0.03125 0.125 -0.078125 0.203125q-0.03125 0.0625 -0.09375 0.125q-0.046875 0.0625 -0.296875 0.171875q-0.234375 0.109375 -0.59375 0.234375q-0.359375 0.109375 -0.8125 0.1875q-0.453125 0.09375 -0.953125 0.09375q-0.84375 0 -1.484375 -0.203125q-0.640625 -0.21875 -1.078125 -0.640625q-0.4375 -0.421875 -0.671875 -1.015625q-0.21875 -0.59375 -0.21875 -1.328125q0 -0.703125 0.15625 -1.375q0.15625 -0.671875 0.453125 -1.28125q0.3125 -0.609375 0.75 -1.109375q0.4375 -0.515625 1.0 -0.890625q0.578125 -0.375 1.25 -0.578125q0.6875 -0.21875 1.484375 -0.21875q0.453125 0 0.84375 0.078125q0.40625 0.078125 0.71875 0.203125q0.3125 0.109375 0.515625 0.25q0.21875 0.125 0.296875 0.203125q0.078125 0.0625 0
 .109375 0.140625q0.03125 0.0625 0.03125 0.15625zm6.9862976 0.84375q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0
  -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.424255 6.09375q0.03125 0.140625 0.015625 0.234375q-0.015625 0.078125 -0.078125 0.125q-0.046875 0.046875 -0.1875 0.046875q-0.125 0.015625 -0.34375 0.015625q-0.140625 0 -0.25 -0.015625q-0.109375 0 -0.171875 -0.015625q-0.046875 -0.03125 -0.078125 -0.0625q-0.015625 -0.046875 -0.03125 -0.09375l-0.3125 -2.0625l-3.5 0l-1.09375 2.03125q-0.046875 0.078125 -0.09375 0.
 125q-0.03125 0.03125 -0.109375 0.0625q-0.078125 0.015625 -0.203125 0.015625q-0.109375 0.015625 -0.28125 0.015625q-0.203125 0 -0.3125 -0.015625q-0.125 -0.015625 -0.15625 -0.046875q-0.046875 -0.046875 -0.03125 -0.140625q0.015625 -0.09375 0.09375 -0.234375l4.375 -7.8125q0.046875 -0.078125 0.09375 -0.125q0.0625 -0.046875 0.140625 -0.0625q0.09375 -0.03125 0.21875 -0.03125q0.125 -0.015625 0.3125 -0.015625q0.21875 0 0.34375 0.015625q0.140625 0 0.21875 0.03125q0.09375 0.015625 0.125 0.0625q0.03125 0.046875 0.046875 0.125l1.25 7.828125zm-2.1875 -6.90625l0 0l-2.28125 4.1875l2.921875 0l-0.640625 -4.1875zm9.930588 0.859375q0 0.34375 -0.078125 0.71875q-0.078125 0.375 -0.265625 0.734375q-0.171875 0.359375 -0.4375 0.6875q-0.265625 0.328125 -0.65625 0.578125q-0.375 0.234375 -0.875 0.375q-0.5 0.140625 -1.1875 0.140625l-1.15625 0l-0.59375 3.046875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-
 0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.546875 -7.78125q0.046875 -0.265625 0.203125 -0.375q0.171875 -0.109375 0.390625 -0.109375l1.65625 0q0.328125 0 0.578125 0.03125q0.25 0.015625 0.484375 0.0625q0.359375 0.078125 0.640625 0.25q0.28125 0.15625 0.46875 0.40625q0.203125 0.234375 0.296875 0.546875q0.109375 0.3125 0.109375 0.6875zm-1.1875 0.109375q0 -0.40625 -0.203125 -0.6875q-0.1875 -0.296875 -0.609375 -0.40625q-0.15625 -0.046875 -0.34375 -0.0625q-0.1875 -0.015625 -0.40625 -0.015625l-1.046875 0l-0.671875 3.375l1.0625 0q0.46875 0 0.78125 -0.09375q0.328125 -0.109375 0.5625 -0.28125q0.25 -0.171875 0.40625 -0.390625q0.171875 -0.234375 0.265625 -0.46875q0.109375 -0.25 0.15625 -0.5q0.046875 -0.25 0.046875 -0.46875zm7.76033 6.171875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0
 .015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l0.734375 -3.75l-3.828125 0l-0.734375 3.75q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l1.609375 -8.109375q0.015625 -0.03125 0.046875 -0.0625q0.046875 -0.046875 0.109375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.265625 0.015625q0.109375 0.015625 0.15625 0.046875q0.0625 0.015625 0.078125 0.0625q0.015625 0.03125 0.015625 0.0625l-0.671875 3.390625l3.828125 0l0.671875 -3.390625q0.015625 -0.03125 0.046875 -0.0625q0.03125 -0.046875 0.09375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.28125 -0.015625q0.15625 0 0.25 0.015625q0.109375 0.015625 0.171875 0.046875q0.0625 
 0.015625 0.078125 0.0625q0.015625 0.03125 0 0.0625l-1.609375 8.109375zm7.3821716 1.890625q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625 -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm7.497406 -9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.03125 0.078125 -0.078125 0.15625q-0.03125 0.0625 -0.078125 0.109375q-0.046875 0.046875 -0.125 0.046875l-3.078125 0l-0.5625 2.859375l2.90625 0q0.078125 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.140625q0 0.0625 -0.015625 0.140625q0 0.0625 -0.015625 0.140625q-
 0.015625 0.0625 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.90625 0l-0.703125 3.5q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0.015625q-0.109375 -0.015625 -0.171875 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.109375l1.5625 -7.796875q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l3.78125 0q0.09375 0 0.125 0.0625q0.03125 0.0625 0.03125 0.15625zm6.3270416 0q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.07812
 5 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm6.7109985 7.734375q-0.015625 0.140625 -0.078125 0.234375q-0.0625 0.078125 -0.140625 0.140625q-0.0625 0.0625 -0.15625 0.09375q-0.078125 0.015625 -0.171875 0.015625l-0.421875 0q-0.171875 0 -0.296875 -0.03125q-0.109375 -0.046875 -0.203125 -0.140625q-0.078125 -0.09375 -0.15625 -0.234375q-0.0625 -0
 .15625 -0.140625 -0.390625l-1.578125 -4.484375q-0.15625 -0.484375 -0.328125 -0.953125q-0.15625 -0.484375 -0.296875 -0.96875l-0.015625 0q-0.078125 0.53125 -0.1875 1.0625q-0.09375 0.515625 -0.203125 1.046875l-0.96875 4.90625q-0.015625 0.0625 -0.0625 0.109375q-0.03125 0.03125 -0.09375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.140625 0 -0.25 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.0625 -0.046875q-0.015625 -0.046875 0 -0.109375l1.546875 -7.765625q0.046875 -0.265625 0.21875 -0.375q0.171875 -0.109375 0.34375 -0.109375l0.5 0q0.15625 0 0.265625 0.03125q0.125 0.03125 0.203125 0.125q0.09375 0.078125 0.15625 0.21875q0.078125 0.125 0.15625 0.328125l1.59375 4.5625q0.140625 0.421875 0.28125 0.84375q0.15625 0.421875 0.296875 0.84375l0.015625 0q0.09375 -0.53125 0.203125 -1.09375q0.109375 -0.578125 0.203125 -1.109375l0.921875 -4.5625q0.015625 -0.0625 0.046875 -0.09375q0.03125 -0.03125 0.09375 -0.0625q0.0625 -0.03125 0.15
 625 -0.03125q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.25 0.015625q0.109375 0 0.15625 0.03125q0.0625 0.03125 0.078125 0.0625q0.015625 0.03125 0.015625 0.09375l-1.5625 7.765625zm9.090393 -7.09375q0 0.140625 -0.046875 0.34375q-0.046875 0.203125 -0.125 0.328125q-0.078125 0.109375 -0.15625 0.109375q-0.09375 0 -0.21875 -0.125q-0.125 -0.125 -0.328125 -0.265625q-0.203125 -0.140625 -0.53125 -0.25q-0.328125 -0.125 -0.828125 -0.125q-0.546875 0 -1.0 0.203125q-0.4375 0.203125 -0.8125 0.5625q-0.359375 0.34375 -0.640625 0.796875q-0.265625 0.453125 -0.453125 0.953125q-0.171875 0.5 -0.265625 1.015625q-0.078125 0.5 -0.078125 0.953125q0 0.515625 0.125 0.921875q0.125 0.40625 0.375 0.6875q0.25 0.28125 0.609375 0.421875q0.359375 0.140625 0.8125 0.140625q0.515625 0 0.875 -0.109375q0.375 -0.109375 0.625 -0.25q0.265625 -0.140625 0.4375 -0.25q0.1875 -0.125 0.296875 -0.125q0.078125 0 0.109375 0.0625q0.03125 0.046875 0.03125 0.15625q0 0.03125 -0.015625 0.09375q-0.015625 0.0625 -0.03125 0.14062
 5q0 0.0625 -0.015625 0.15625q-0.015625 0.078125 -0.046875 0.15625q-0.03125 0.0625 -0.0625 0.125q-0.015625 0.0625 -0.09375 0.125q-0.0625 0.0625 -0.28125 0.203125q-0.21875 0.125 -0.53125 0.234375q-0.3125 0.109375 -0.71875 0.1875q-0.390625 0.09375 -0.828125 0.09375q-0.671875 0 -1.203125 -0.203125q-0.53125 -0.203125 -0.90625 -0.578125q-0.375 -0.390625 -0.578125 -0.96875q-0.203125 -0.578125 -0.203125 -1.328125q0 -0.609375 0.125 -1.265625q0.140625 -0.65625 0.390625 -1.265625q0.25 -0.625 0.625 -1.171875q0.390625 -0.546875 0.890625 -0.953125q0.5 -0.421875 1.125 -0.65625q0.640625 -0.25 1.390625 -0.25q0.484375 0 0.890625 0.109375q0.421875 0.109375 0.703125 0.28125q0.296875 0.15625 0.421875 0.28125q0.140625 0.125 0.140625 0.296875zm6.2602844 -0.640625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.07
 8125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.60935974 3.0l3.3437347 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.0468597 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.4999847 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875z"
+       fill-rule="nonzero"
+       id="path258" />
+    <path
+       fill="#000000"
+       d="m172.59329 223.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859
 375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.250732 0l0 -9.546875l3.59375 0q1.09375 0 1.75 0.296875q0.65625 0.28125 1.03125 0.890625q0.375 0.609375 0.375 1.265625q0 0.609375 -0.34375 1.15625q-0.328125 0.53125 -0.984375 0.859375q0.859375 0.25 1.328125 0.875q0.46875 0.609375 0.
 46875 1.4375q0 0.671875 -0.296875 1.25q-0.28125 0.578125 -0.703125 0.890625q-0.40625 0.3125 -1.03125 0.46875q-0.625 0.15625 -1.546875 0.15625l-3.640625 0zm1.265625 -5.53125l2.0625 0q0.84375 0 1.203125 -0.109375q0.484375 -0.140625 0.71875 -0.46875q0.25 -0.34375 0.25 -0.84375q0 -0.46875 -0.234375 -0.828125q-0.21875 -0.359375 -0.640625 -0.5q-0.421875 -0.140625 -1.453125 -0.140625l-1.90625 0l0 2.890625zm0 4.40625l2.375 0q0.609375 0 0.859375 -0.046875q0.4375 -0.078125 0.734375 -0.25q0.296875 -0.1875 0.484375 -0.53125q0.1875 -0.359375 0.1875 -0.8125q0 -0.53125 -0.28125 -0.921875q-0.265625 -0.40625 -0.75 -0.5625q-0.484375 -0.15625 -1.40625 -0.15625l-2.203125 0l0 3.28125zm12.06163 1.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 
 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm3.1624756 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm8.156113 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625
  0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path260" />
+    <path
+       fill="#000000"
+       d="m186.7589 261.9118l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896713 -0.5781
 25q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875z"
+       fill-rule="nonzero"
+       id="path262" />
+    <path
+       fill="#000000"
+       d="m163.32709 275.55243l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path264" />
+    <path
+       fill="#000000"
+       d="m186.7589 313.10867l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 
 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318588 4.125
 l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125z"
+       fill-rule="nonzero"
+       id="path266" />
+    <path
+       fill="#000000"
+       d="m163.32709 326.7493l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.42187
 5 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125 
 0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -
 1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125
 q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125 
 1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.0156
 25 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-
 0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path268" />
+    <path
+       fill="#000000"
+       d="m186.7589 364.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052963 3.0l0 
 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0z"
+       fill-rule="nonzero"
+       id="path270" />
+    <path
+       fill="#000000"
+       d="m163.32709 377.94614l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path272" />
+    <path
+       fill="#000000"
+       d="m185.65256 415.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625
  0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365463 4.12
 5l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0z"
+       fill-rule="nonzero"
+       id="path274" />
+    <path
+       fill="#000000"
+       d="m163.32709 429.14297l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path276" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m92.06693 62.29134l230.33072 0l0 27.464565l-230.33072 0z"
+       fill-rule="evenodd"
+       id="path278" />
+    <path
+       fill="#000000"
+       d="m114.3782 84.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0164948 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.8748627 -1.171875l1.2031174 0.140625q-0.28125 1.0625 -1.0624924 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375
  -0.96875q1.390625 0 2.265625 0.9375q0.8749924 0.9375 0.8749924 2.65625q0 0.109375 0 0.3125l-5.1562424 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.443718 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.271851 -2.078125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125 2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.
 390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm6.6312256 3.578125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -
 0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.45
 3125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm10.677963 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -
 0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.17984 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.43
 75zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875zm4.8434753 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836807 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0
 .9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 
 1.453125l0 3.578125l-1.171875 0zm10.664932 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071198 2.65625l-0.125 -1.09375q0.375 0.109375 0.65
 625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625
 q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875
  0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1
 .234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path280"
+       style="fill:#c8ab37" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925z"
+       fill-rule="evenodd"
+       id="path282" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path284" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path286" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m64.91338 302.8189l99.40157 0l0 27.46457l-99.40157 0z"
+       fill-rule="evenodd"
+       id="path288" />
+    <path
+       fill="#000000"
+       d="m74.71026 323.3389l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.
 609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm7.658203 -2.390625l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.283203 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625
  -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm10.017578 3.109375l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm10.220703 1.109375l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2
 .390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -
 0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path290" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m687.0105 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path292" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m815.45404 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path294" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 100.52126l129.44092 0"
+       fill-rule="nonzero"
+       id="path296" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 135.71811l129.44092 0"
+       fill-rule="nonzero"
+       id="path298" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 172.91496l129.44092 0"
+       fill-rule="nonzero"
+       id="path300" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 208.11182l129.44092 0"
+       fill-rule="nonzero"
+       id="path302" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 243.30865l129.44092 0"
+       fill-rule="nonzero"
+       id="path304" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 278.50552l129.44092 0"
+       fill-rule="nonzero"
+       id="path306" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 313.70236l129.44092 0"
+       fill-rule="nonzero"
+       id="path308" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 348.8992l129.44092 0"
+       fill-rule="nonzero"
+       id="path310" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 384.09607l129.44092 0"
+       fill-rule="nonzero"
+       id="path312" />
+    <path
+       fill="#000000"
+       d="m733.8046 122.32126l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 
 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.9687
 5 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path314" />
+    <path
+       fill="#000000"
+       d="m709.2222 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm9.155334 3.0625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm2.5392456 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.9281006 3.453125l-2.125 -6.90625l1.21875 0l1.09375 3.984375l0.421875 1.484375q0.015625 -0.109375 0.359375 -1.421875l1.09375 -4.046875l1.203125 0l1.03125 4.0l0.34375 1.328125l0.40
 625 -1.34375l1.171875 -3.984375l1.140625 0l-2.15625 6.90625l-1.21875 0l-1.09375 -4.140625l-0.265625 -1.171875l-1.40625 5.3125l-1.21875 0zm8.343872 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.60
 9375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.031
 25q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6311035 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.6
 09375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0
 625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q
 -1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.
 53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path316" />
+    <path
+       fill="#000000"
+       d="m710.1765 191.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.234497 -0.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375
  -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6468506 3.453125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm9.974976 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.
 171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874878 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-
 0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.
 296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.2031
 25q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.87
 5 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625z"
+       fill-rule="nonzero"
+       id="path318" />
+    <path
+       fill="#000000"
+       d="m711.206 229.9118l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.438232 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-
 0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.5218506 1.40625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.
 265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 3.703125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125
  0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875
 q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0
 .140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6310425 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.187
 5q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.188416 -2.21875l1.203125 0.140625q-0.281
 25 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125
  0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path320" />
+    <path
+       fill="#000000"
+       d="m706.683 265.10867l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.14
 0625l-0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765
 625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1
 .90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.8967285 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.56251526 1.71875 -0.56251526q0.78125 0 1.359375 0.3125q0.578125 0.29689026 0.953125 0.89064026q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.
 21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm10.2404785 7.359375l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.
 671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path322" />
+    <path
+       fill="#000000"
+       d="m706.683 300.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.375
  -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42
 1875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.3186035 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm7.0217285 2.65625l0 -9.5625l1.07812
 5 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.0
 78125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path324" />
+    <path
+       fill="#000000"
+       d="m706.683 335.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.37
 5 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 
 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4
 21875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052979 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375
  -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm5.2873535 3.78125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.7
 5 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path326" />
+    <path
+       fill="#000000"
+       d="m705.57666 370.69922l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218872 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.
 375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843506 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.42187
 5 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1
 .421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm13.1875 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875
  0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.062
 5 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path328" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m636.06696 70.291336l230.33069 0l0 27.46457l-230.33069 0z"
+       fill-rule="evenodd"
+       id="path330" />
+    <path
+       fill="#000000"
+       d="m660.59735 92.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0165405 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874817 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.
 96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.4437256 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.490601 -2.65625l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.9
 0625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.07
 8125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.7873535 0.671875q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0
 .921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359
 375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.179871 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q
 0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.26
 5625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.07812
 5zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.5
 78125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4
 375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.4923096 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0
 .375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 
 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.7
 03125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path332"
+       style="fill:#008033" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013z"
+       fill-rule="evenodd"
+       id="path334" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path336" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path338" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m821.979 278.50656l99.40155 0l0 27.46457l-99.40155 0z"
+       fill-rule="evenodd"
+       id="path340" />
+    <path
+       fill="#000000"
+       d="m831.7759 299.02655l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0
 .609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm11.908203 -4.390625l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.908203 3.703125l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q
 0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm5.767578 3.625l1.03125 0.15625q0.0625 0.46875 0.359375 0.6875q0.390625 0.296875 1.0625 0.296875q0.734375 0 1.125 -0.296875q0.40625 -0.296875 0.546875 -0.8125q0.09375 -0.328125 0.078125 -1.359375q-0.6875 0.8125 -1.71875 0.8125q-1.28125 0 -1.984375 -0.921875q-0.703125 -0.9375 -0.703125 -2.21875q0 -0.890625 0.3125 -1.640625q0.328125 -0.765625 0.9375 -1.171875q0.609375 -0.40625 1.4375 -0.40625q1.109375 0 1.828125 0.890625l0 -0.75l0.96875 0l0 5.375q0 1.453125 -0.296875 2.0625q-0.296875 0.609375 -0.9375 0.953125q-0.640625 0.359375 -1.578125 0.359375q-1.109375 0 -1.796875 -0.5q-0.6875 -0.5 -0.671875 -1.515625zm0.875 -3.734375q0 1.21875 0.484375 1.7
 8125q0.484375 0.5625 1.21875 0.5625q0.734375 0 1.21875 -0.5625q0.5 -0.5625 0.5 -1.75q0 -1.140625 -0.515625 -1.71875q-0.5 -0.578125 -1.21875 -0.578125q-0.703125 0 -1.203125 0.578125q-0.484375 0.5625 -0.484375 1.6875zm10.251953 1.21875l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.35937
 5 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path342" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m359.4252 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path344" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m520.34906 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path346" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 100.52126l161.92126 0"
+       fill-rule="nonzero"
+       id="path348" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 135.71811l161.92126 0"
+       fill-rule="nonzero"
+       id="path350" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 170.91496l161.92126 0"
+       fill-rule="nonzero"
+       id="path352" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 206.11182l161.92126 0"
+       fill-rule="nonzero"
+       id="path354" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 241.30865l161.92126 0"
+       fill-rule="nonzero"
+       id="path356" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 276.50552l161.92126 0"
+       fill-rule="nonzero"
+       id="path358" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 311.70236l161.92126 0"
+       fill-rule="nonzero"
+       id="path360" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 346.8992l161.92126 0"
+       fill-rule="nonzero"
+       id="path362" />
+    <path
+       fill="#000000"
+       d="m379.84702 119.25876l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.1937256 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359
 375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.802948 1.25q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.4531
 25l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-
 0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.906
 25 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path364" />
+    <path
+       fill="#000000"
+       d="m379.84702 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.615601 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm6.584198 -3.453125q0 -1.921875 1.07812
 5 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0
 .125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.3437
 5 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.17
 1875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path366" />
+    <path
+       fill="#000000"
+       d="m376.8892 189.65247l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921
 875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.
 546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.
 328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42187
 5 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.7500305 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921
 875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0
 .484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03
 125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203
 125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path368" />
+    <path
+       fill="#000000"
+       d="m372.88116 227.9118l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896698 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.8029785 1.2
 5q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0
 .734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.
 34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm
 9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path370" />
+    <path
+       fill="#000000"
+       d="m372.88116 263.10867l0 -9.54689l6.90625 0l0 1.125l-5.640625 0l0 2.9218903l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5000153l0 7.5000153l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.5
 78125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.87
 5 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318573 4.125l-1.171875 0l0 -7.4687653q-0.421875 0.40626526 -1.109375 0.81251526q-0.6875 0.40625 -1.234375 0.609375l0 -1.1406403q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.57814zm6.5842285 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.6562
 5q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.
 359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.
 15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.7187653l1.171875 -0.703125l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path372" />
+    <path
+       fill="#000000"
+       d="m372.88116 298.3055l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052948 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm4.8498535 -2.328
 125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 
 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-
 0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375
 zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path374" />
+    <path
+       fill="#000000"
+       d="m371.7748 333.50235l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.7178955 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.75 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65
 625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625
  -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0
 .9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path376" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.77692 65.05774l280.09448 0l0 27.46457l-280.09448 0z"
+       fill-rule="evenodd"
+       id="path378" />
+    <path
+       fill="#000000"
+       d="m345.39603 83.51399l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.8593
 75 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.969635 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 0l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578
 125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836792 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0
 .8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.6649475 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.42187
 5q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-
 2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875
  0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.9218
 75 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.1093
 75q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.1247253 1.046875l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9842224 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.4531
 25 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 
 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm11.084351 1.203125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.521881 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.187
 5 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.896851 0l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm6.6468506 -4.734
 375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.9454346 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm7.1937256 0.578125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125
  2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm9.8811035 1.515625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.
 078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.
 34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375
  -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875
  -1.171875l0 -0.421875zm2.9906006 3.46875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.633667 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.2
 5 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625z"
+       fill-rule="nonzero"
+       id="path380"
+       style="fill:#88aa00" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m516.2336 178.6483l115.4646 0l0 27.464554l-115.4646 0z"
+       fill-rule="evenodd"
+       id="path382" />
+    <path
+       fill="#000000"
+       d="m532.2961 196.15266l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm5.9960938 -1.859375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.062
 5 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5zm8.71875 0.921875l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm5.0996094 0.171875q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.
 265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.35937
 5 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125z"
+       fill-rule="nonzero"
+       id="path384" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m589.60895 206.11285l-61.259888 0"
+       fill-rule="evenodd"
+       id="path386" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m589.60895 206.11285l-55.259888 0"
+       fill-rule="evenodd"
+       id="path388"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m534.34906 204.46114l-4.538086 1.6517181l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path390" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m520.7349 322.6483l115.46454 0l0 27.46457l-115.46454 0z"
+       fill-rule="evenodd"
+       id="path392" />
+    <path
+       fill="#000000"
+       d="m536.7974 340.15268l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm10.667969 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.876953 3.703125l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0
 .765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.705078 0l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875z"
+       fill-rule="nonzero"
+       id="path394" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m586.3438 346.90027l-61.259827 0"
+       fill-rule="evenodd"
+       id="path396" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m586.3438 346.90027l-55.259827 0"
+       fill-rule="evenodd"
+       id="path398"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m531.084 345.24854l-4.538086 1.6517334l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path400" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.45407 204.69554l85.51181 -102.645676"
+       fill-rule="evenodd"
+       id="path402" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.45407 204.69554l81.67139 -98.03577"
+       fill-rule="evenodd"
+       id="path404" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m354.39453 107.716995l1.6356201 -4.5439224l-4.1737366 2.4294815z"
+       fill-rule="evenodd"
+       id="path406" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 240.03412l88.0 105.60629"
+       fill-rule="evenodd"
+       id="path408" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 240.03412l84.15903 100.99686"
+       fill-rule="evenodd"
+       id="path410" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m353.94522 342.08835l4.1740417 2.4289856l-1.6362 -4.5437317z"
+       fill-rule="evenodd"
+       id="path412" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 392.81104l411.87402 -290.77167"
+       fill-rule="evenodd"
+       id="path414" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 392.81104l406.9724 -287.31128"
+       fill-rule="evenodd"
+       id="path416" />
+    <path
+       fill="#274e13"
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m678.98016 106.84912l2.7546997 -3.9666214l-4.659912 1.2679062z"
+       fill-rule="evenodd"
+       id="path418" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 442.09448l415.9685 -61.102356"
+       fill-rule="evenodd"
+       id="path420" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 442.09448l410.03223 -60.230347"
+       fill-rule="evenodd"
+       id="path422" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m681.3274 383.49832l4.249878 -2.2937317l-4.7299805 -0.9746704z"
+       fill-rule="evenodd"
+       id="path424" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/link_the_nodes.svg b/doc/guides/prog_guide/img/link_the_nodes.svg
new file mode 100644
index 000000000..4a127e67c
--- /dev/null
+++ b/doc/guides/prog_guide/img/link_the_nodes.svg
@@ -0,0 +1,3330 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg1003"
+   sodipodi:docname="Link_the_nodes.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata1009">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs1007" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview1005"
+     showgrid="false"
+     inkscape:zoom="1.2361274"
+     inkscape:cx="1097.0658"
+     inkscape:cy="171.4074"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1003" />
+  <clipPath
+     id="g7c2ed6c54d_0_300.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path2" />
+  </clipPath>
+  <g
+     clip-path="url(#g7c2ed6c54d_0_300.0)"
+     id="g1001">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path5" />
+    <path
+       fill="#ffffff"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path7" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path9" />
+    <path
+       fill="#ffffff"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path11" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path13" />
+    <path
+       fill="#fdf8f8"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path15" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path17" />
+    <path
+       fill="#d9ead3"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path19" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path21" />
+    <path
+       fill="#cfe2f3"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path23" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path25" />
+    <path
+       fill="#f4cccc"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path27" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path29" />
+    <path
+       fill="#f4cccc"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path31" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path33" />
+    <path
+       fill="#eeeeee"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path35" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path37" />
+    <path
+       fill="#eeeeee"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path39" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path41" />
+    <path
+       fill="#eeeeee"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path43" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path45" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m199.20312 266.6246l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path47" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482z"
+       fill-rule="evenodd"
+       id="path49" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path51" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path53" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m191.56721 277.2064l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path55" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path57" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path59" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path61" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path63" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path65" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path67" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path69" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path71" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path73" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path75" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m145.16678 229.68527l73.13281 0l0 16.320053l-73.13281 0z"
+       fill-rule="evenodd"
+       id="path77" />
+    <path
+       fill="#000000"
+       d="m164.66463 247.24533l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827484 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.54685974 -0.375 -0.85935974 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.82810974 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.9531097 -2.75q0 1.046875 0.43748474 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.42185974 0.5 -0.42185974 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.5
 15625 1.40625q0.46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.78125q0.25 -0.40625 0.6875 -0.65625q0.4375 -0.25 0.984375 -0.25q0.609375 0 1.0 0.265625q0.390625 0.25 0.5625 0.703125q0.65625 -0.96875 1.703125 -0.96875q0.828125 0 1.265625 0.46875q0.4375 0.453125 0.4375 1.390625l0 3.796875l-0.921875 0l0 -3.484375q0 -0.5625 -0.09375 -0.796875q-0.09375 -0.25 -0.34375 -0.40625q-0.234375 -0.15625 -0.546875 -0.15625q-0.59375 0 -0.984375 0.390625q-0.375 0.390625 -0.375 1.25l0 3.203125l-0.9375 0l0 -3.59375q0 -0.625 -0.234375 -0.9375q-0.21875 -0.3125 -0.75 -0.3125q-0.390625 0 -0.734375 0.21875q-0.328125 0.203125 -0.484375 0.609375q-0.140625 0.390625 -0.140625 1.15625l0 2.859375l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path79" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path81" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path83" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path85" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path87" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path89" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path91" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path93" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path95" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path97" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path99" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path101" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path103" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path105" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path107" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path109" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path111" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path113" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path115" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path117" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path119" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 186.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path121" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path123" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path125" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path127" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 197.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path129" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path131" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path133" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path135" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path137" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path139" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path141" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path143" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path145" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path147" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path149" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 149.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path151" />
+    <path
+       fill="#000000"
+       d="m286.14026 167.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path153" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path155" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path157" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path159" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path161" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path163" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path165" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path167" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path169" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path171" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path173" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path175" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path177" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path179" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path181" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path183" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path185" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path187" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path189" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path191" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path193" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 346.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path195" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path197" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path199" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path201" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 357.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path203" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path205" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path207" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path209" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path211" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path213" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path215" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path217" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path219" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path221" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path223" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 309.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path225" />
+    <path
+       fill="#000000"
+       d="m286.14026 327.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.840271 0.53125q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.46875 1.578125z"
+       fill-rule="nonzero"
+       id="path227" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path229" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path231" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path233" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path235" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path237" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path239" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path241" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path243" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path245" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path247" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path249" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path251" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path253" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path255" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path257" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path259" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path261" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path263" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path265" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path267" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 442.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path269" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path271" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path273" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path275" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 453.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path277" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path279" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path281" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path283" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path285" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path287" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path289" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path291" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path293" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path295" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path297" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 405.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path299" />
+    <path
+       fill="#000000"
+       d="m406.43945 423.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.809021 1.640625l0.921875 -0.140625q0.078125 0.5625 0.4375 0.859375q0.359375 0.296875 1.0 0.296875q0.640625 0 0.953125 -0.265625q0.3125 -0.265625 0.3125 -0.625q0 -0.3125 -0.28125 -0.5q-0.1875 -0.125 -0.953125 -0.3125q-1.03125 -0.265625 -1.4375 -0.453125q-0.390625 -0.1875 -0.59375 -0.515625q-0.203125 -0.34375 -0.203125 -0.75q0 -0.359375 0.171875 -0.671875q0.171875 -0.328125 0.453125 -0.53125q0.21875 -0.15625 0.59375 -0.265625q0.390625 -0.125 0.8125 -0.125q0.65625 0 1.140625 0.1875q0.5 0.1875 0.734375 0.515625q0.234375 0.3125 0.3125 0.859375l-0.90625 0.125q-0.0625 -0.4375 -0.375 -0.671875q-0.296875 -0.234375 -0.828125 -0.234375q-0.65625 0 -0.9375 0.21875q-0.265625 0.203125 -0.265625 0.484375q0 0.1875 0.109375 0.328125
 q0.125 0.15625 0.359375 0.25q0.140625 0.0625 0.828125 0.25q1.0 0.265625 1.390625 0.4375q0.390625 0.15625 0.609375 0.484375q0.234375 0.3125 0.234375 0.796875q0 0.46875 -0.28125 0.890625q-0.265625 0.40625 -0.78125 0.640625q-0.515625 0.21875 -1.171875 0.21875q-1.078125 0 -1.640625 -0.4375q-0.5625 -0.453125 -0.71875 -1.34375z"
+       fill-rule="nonzero"
+       id="path301" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path303" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path305" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path307" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path309" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path311" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path313" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path315" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path317" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path319" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path321" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path323" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path325" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path327" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path329" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path331" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path333" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path335" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path337" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path339" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path341" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 314.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path343" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path345" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path347" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path349" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 325.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path351" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path353" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path355" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path357" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path359" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path361" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path363" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path365" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path367" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path369" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path371" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 277.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path373" />
+    <path
+       fill="#000000"
+       d="m407.32922 295.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.582733 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.168396 3.296875l0 -5.53125l0.84375 0l0 0.84375q0.328125 -0.59375 0.59375 -0.78125q0.28125 -0.1875 0.609375 -0.1875q0.46875 0 0.953125 0.3125l-0.3125 0.859375q-0.34375 -0.203125 -0.6875 -0.203125q-0.3125 0 -0.5625 0.1875q-0.234375 0.1875 -0.34375 0.515625q-0.15625 0.5 -0.15625 1.09375l0 2.890625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path375" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path377" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path379" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path381" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path383" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path385" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path387" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path389" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path391" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path393" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path395" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path397" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path399" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path401" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path403" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path405" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path407" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path409" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path411" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path413" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path415" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 210.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path417" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path419" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path421" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path423" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 221.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path425" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path427" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path429" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path431" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path433" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path435" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path437" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path439" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path441" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path443" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path445" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 173.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path447" />
+    <path
+       fill="#000000"
+       d="m414.14026 191.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.699646 5.421875l0 -2.71875q-0.21875 0.3125 -0.609375 0.515625q-0.390625 0.203125 -0.828125 0.203125q-0.984375 0 -1.703125 -0.78125q-0.703125 -0.796875 -0.703125 -2.15625q0 -0.828125 0.28125 -1.484375q0.296875 -0.671875 0.84375 -1.015625q0.546875 -0.34375 1.203125 -0.34375q1.03125 0 1.609375 0.875l0 -0.75l0.84375 0l0 7.65625l-0.9375 0zm-2.875 -4.90625q0 1.0625 0.4375 1.609375q0.453125 0.53125 1.078125 0.53125q0.59375 0 1.015625 -0.5q0.4375 -0.515625 0.4375 -1.546875q0 -1.109375 -0.453125 -1.65625q-0.453125 -0.5625 -1.0625 -0.5625q-0.609375 0 -1.03125 0.515625q-0.421875 0.515625 -0.421875 1.609375z"
+       fill-rule="nonzero"
+       id="path449" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path451" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path453" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path455" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path457" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path459" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path461" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path463" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path465" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path467" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path469" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path471" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path473" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path475" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path477" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path479" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path481" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path483" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path485" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path487" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path489" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 106.62759l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path491" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path493" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path495" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path497" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path499" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path501" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path503" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path505" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path507" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path509" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path511" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path513" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path515" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path517" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path519" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path521" />
+    <path
+       fill="#000000"
+       d="m414.14026 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 5.421875l0 -7.65625l0.859375 0l0 0.71875q0.296875 -0.421875 0.671875 -0.625q0.390625 -0.21875 0.921875 -0.21875q0.703125 0 1.25 0.375q0.546875 0.359375 0.8125 1.03125q0.28125 0.65625 0.28125 1.453125q0 0.84375 -0.3125 1.53125q-0.296875 0.671875 -0.875 1.03125q-0.578125 0.359375 -1.21875 0.359375q-0.46875 0 -0.84375 -0.1875q-0.375 -0.203125 -0.609375 -0.515625l0 2.703125l-0.9375 0zm0.84375 -4.859375q0 1.0625 0.4375 1.578125q0.4375 0.515625 1.046875 0.515625q0.625 0 1.0625 -0.53125q0.453125 -0.53125 0.453125 -1.640625q0 -1.046875 -0.4375 -1.578125q-0.4375 -0.53125 -1.046875 -0.53125q-0.59375 0 -1.0625 0.5625q-0.453125 0.5625 -0.453125 1.625z"
+       fill-rule="nonzero"
+       id="path523" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path525" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path527" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path529" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path531" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path533" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path535" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path537" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path539" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path541" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path543" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path545" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path547" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path549" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path551" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path553" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path555" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path557" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path559" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path561" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path563" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path565" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path567" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path569" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path571" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path573" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path575" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path577" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path579" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path581" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path583" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path585" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path587" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path589" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path591" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path593" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path595" />
+    <path
+       fill="#000000"
+       d="m566.14026 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.809021 3.296875l0 -0.8125q-0.65625 0.9375 -1.75 0.9375q-0.5 0 -0.921875 -0.1875q-0.421875 -0.1875 -0.625 -0.46875q-0.203125 -0.28125 -0.296875 -0.703125q-0.046875 -0.265625 -0.046875 -0.875l0 -3.421875l0.9375 0l0 3.0625q0 0.734375 0.046875 1.0q0.09375 0.359375 0.375 0.578125q0.296875 0.203125 0.703125 0.203125q0.421875 0 0.796875 -0.203125q0.375 -0.21875 0.515625 -0.59375q0.15625 -0.375 0.15625 -1.078125l0 -2.96875l0.9375 0l0 5.53125l-0.828125 0z"
+       fill-rule="nonzero"
+       id="path597" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path599" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path601" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path603" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path605" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path607" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path609" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path611" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path613" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path615" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path617" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path619" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path621" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path623" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path625" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path627" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path629" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path631" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path633" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path635" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path637" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 298.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path639" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path641" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path643" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path645" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 309.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path647" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path649" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path651" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path653" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path655" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path657" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path659" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path661" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path663" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path665" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path667" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 261.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path669" />
+    <path
+       fill="#000000"
+       d="m566.43945 279.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm9.715271 3.296875l-2.09375 -5.53125l0.984375 0l1.1875 3.3125q0.1875 0.53125 0.359375 1.109375q0.109375 -0.4375 0.34375 -1.046875l1.21875 -3.375l0.96875 0l-2.09375 5.53125l-0.875 0z"
+       fill-rule="nonzero"
+       id="path671" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path673" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path675" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path677" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path679" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path681" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path683" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path685" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path687" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path689" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path691" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path693" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path695" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path697" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path699" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path701" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path703" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path705" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path707" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path709" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path711" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 434.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path713" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path715" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path717" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path719" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 445.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path721" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path723" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path725" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path727" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path729" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path731" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path733" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path735" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path737" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path739" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path741" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 397.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path743" />
+    <path
+       fill="#000000"
+       d="m566.43945 415.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.559021 3.296875l2.015625 -2.875l-1.859375 -2.65625l1.171875 0l0.84375 1.296875q0.234375 0.375 0.390625 0.625q0.21875 -0.34375 0.40625 -0.609375l0.9375 -1.3125l1.125 0l-1.921875 2.609375l2.0625 2.921875l-1.15625 0l-1.125 -1.71875l-0.296875 -0.46875l-1.453125 2.1875l-1.140625 0z"
+       fill-rule="nonzero"
+       id="path745" />
+    <path
+       fill="#ffffff"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path747" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path749" />
+    <path
+       fill="#ffffff"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path751" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path753" />
+    <path
+       fill="#fdf8f8"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path755" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path757" />
+    <path
+       fill="#d9ead3"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path759" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path761" />
+    <path
+       fill="#cfe2f3"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path763" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path765" />
+    <path
+       fill="#f4cccc"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path767" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path769" />
+    <path
+       fill="#f4cccc"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path771" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path773" />
+    <path
+       fill="#eeeeee"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path775" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path777" />
+    <path
+       fill="#eeeeee"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path779" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path781" />
+    <path
+       fill="#eeeeee"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path783" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path785" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.16473 106.62759l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path787" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path789" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path791" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path793" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m583.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path795" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path797" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path799" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path801" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path803" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path805" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path807" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path809" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path811" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path813" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path815" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m537.1668 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path817" />
+    <path
+       fill="#000000"
+       d="m559.62317 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm10.230896 2.453125l0.125 0.828125q-0.390625 0.09375 -0.703125 0.09375q-0.5 0 -0.78125 -0.15625q-0.28125 -0.171875 -0.40625 -0.4375q-0.109375 -0.265625 -0.109375 -1.109375l0 -3.171875l-0.6875 0l0 -0.734375l0.6875 0l0 -1.359375l0.9375 -0.5625l0 1.921875l0.9375 0l0 0.734375l-0.9375 0l0 3.234375q0 0.390625 0.046875 0.515625q0.046875 0.109375 0.15625 0.1875q0.109375 0.0625 0.328125 0.0625q0.15625 0 0.40625 -0.046875z"
+       fill-rule="nonzero"
+       id="path819" />
+    <path
+       fill="#ffffff"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path821" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path823" />
+    <path
+       fill="#ffffff"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path825" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path827" />
+    <path
+       fill="#fdf8f8"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path829" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path831" />
+    <path
+       fill="#d9ead3"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path833" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path835" />
+    <path
+       fill="#cfe2f3"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path837" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path839" />
+    <path
+       fill="#f4cccc"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path841" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path843" />
+    <path
+       fill="#f4cccc"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path845" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path847" />
+    <path
+       fill="#eeeeee"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path849" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path851" />
+    <path
+       fill="#eeeeee"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path853" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path855" />
+    <path
+       fill="#eeeeee"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path857" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path859" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m783.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path861" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path863" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path865" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path867" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m775.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path869" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path871" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path873" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path875" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path877" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path879" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path881" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path883" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path885" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path887" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path889" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m729.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path891" />
+    <path
+       fill="#000000"
+       d="m750.43945 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.137146 5.421875l-0.09375 -0.875q0.296875 0.078125 0.53125 0.078125q0.3125 0 0.5 -0.109375q0.1875 -0.09375 0.3125 -0.28125q0.078125 -0.140625 0.28125 -0.703125q0.03125 -0.078125 0.078125 -0.21875l-2.09375 -5.546875l1.015625 0l1.140625 3.203125q0.234375 0.609375 0.40625 1.28125q0.15625 -0.640625 0.375 -1.265625l1.1875 -3.21875l0.9375 0l-2.109375 5.625q-0.328125 0.90625 -0.515625 1.25q-0.25 0.46875 -0.578125 0.6875q-0.3125 0.21875 -0.765625 0.21875q-0.265625 0 -0.609375 -0.125z"
+       fill-rule="nonzero"
+       id="path893" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m211.71075 275.29062c16.11023 0 24.165344 -13.937012 32.220474 -27.874023c8.055115 -13.937012 16.110214 -27.874008 32.220474 -27.874008"
+       fill-rule="evenodd"
+       id="path895" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m211.71075 275.29062c16.11023 0 24.16536 -13.937012 32.220474 -27.874008c4.0275574 -6.968506 8.055115 -13.937012 13.089554 -19.163391c2.5172424 -2.6131897 5.286194 -4.790848 8.432709 -6.315201c1.5732727 -0.7621918 3.2409363 -1.3610382 5.018738 -1.7693481c0.4444275 -0.10209656 0.89575195 -0.19224548 1.3542175 -0.27009583c0.22921753 -0.038909912 0.4602356 -0.07473755 0.6930847 -0.107437134l0.2121582 -0.028259277"
+       fill-rule="evenodd"
+       id="path897" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m272.7317 219.76288l-1.0499573 1.1945496l3.011078 -1.3208618l-3.1556702 -0.923645z"
+       fill-rule="evenodd"
+       id="path899" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m206.79628 284.1058c0 19.13388 14.29921 28.700806 28.598434 38.26773c14.29921 9.566925 28.59842 19.13385 28.59842 38.2677"
+       fill-rule="evenodd"
+       id="path901" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m206.79628 284.1058c0 19.13385 14.29921 28.700775 28.59842 38.26773c7.1496124 4.7834473 14.299225 9.566925 19.661423 15.546234c2.6810913 2.989685 4.915344 6.2783203 6.4793396 10.015381c0.7819824 1.8685608 1.3963928 3.8492126 1.8153076 5.9606323c0.20947266 1.0557556 0.37008667 2.144165 0.47827148 3.2676392l0.00491333 0.05441284"
+       fill-rule="evenodd"
+       id="path903" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m263.83395 357.21786l-1.1755981 -1.0711365l1.2668762 3.0341797l0.9798584 -3.1386719z"
+       fill-rule="evenodd"
+       id="path905" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m324.40216 194.87454c0 -41.086624 33.811005 -82.17323 67.62204 -82.17323"
+       fill-rule="evenodd"
+       id="path907" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m324.40216 194.87454c0 -20.543304 8.452759 -41.086624 21.131866 -56.494095c6.339569 -7.7037506 13.735748 -14.123528 21.660187 -18.617378c3.9622498 -2.2469177 8.056519 -4.0123596 12.216888 -5.216072c2.0801697 -0.6018524 4.17688 -1.0632782 6.281769 -1.3742294c0.5262451 -0.07774353 1.0529785 -0.14608002 1.5801392 -0.20485687c0.26358032 -0.029388428 0.5272217 -0.056381226 0.7909546 -0.080963135l0.53601074 -0.045051575"
+       fill-rule="evenodd"
+       id="path909" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.6 112.841896l-1.0775146 1.1697693l3.0410461 -1.2503891l-3.1333008 -0.9968872z"
+       fill-rule="evenodd"
+       id="path911" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.75735 207.661c36.834625 0 73.66928 40.34645 73.66928 80.69292"
+       fill-rule="evenodd"
+       id="path913" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.75735 207.66098c18.417297 0 36.834625 10.086624 50.647614 25.216537c6.906494 7.564972 12.661926 16.390747 16.690704 25.84694c2.0144043 4.728119 3.597168 9.613831 4.6762695 14.578339c0.5395813 2.4822388 0.9532471 4.984192 1.2320251 7.4959717c0.1394043 1.2559204 0.24505615 2.5142822 0.31588745 3.7738953l0.017486572 0.35531616"
+       fill-rule="evenodd"
+       id="path915" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m400.33734 284.92798l-1.1535034 -1.0949097l1.2047119 3.0594177l1.0437012 -3.1180115z"
+       fill-rule="evenodd"
+       id="path917" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m331.69748 203.27638c15.078735 0 22.618134 3.3543396 30.157501 6.708664c7.5393677 3.3543396 15.078735 6.708664 30.15747 6.708664"
+       fill-rule="evenodd"
+       id="path919" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m331.69748 203.2764c15.078766 0 22.618134 3.3543243 30.157501 6.7086487c3.7696838 1.6771698 7.5393677 3.3543396 12.251495 4.612213c2.3560486 0.62893677 4.947693 1.1530457 7.8927917 1.519928c1.4725037 0.18344116 3.0333862 0.32757568 4.6973267 0.42582703c0.41601562 0.024597168 0.83847046 0.046279907 1.2675476 0.06503296l0.62176514 0.024765015"
+       fill-rule="evenodd"
+       id="path921" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.5859 216.63281l-1.1443787 1.1044312l3.109253 -1.0695038l-3.069275 -1.179306z"
+       fill-rule="evenodd"
+       id="path923" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m323.7046 355.2753c36.17325 0 72.346466 -3.1653748 72.346466 -6.330719"
+       fill-rule="evenodd"
+       id="path925" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m323.7046 355.2753c18.08664 0 36.17325 -0.7913208 49.73819 -1.9783325c6.782501 -0.59350586 12.43457 -1.2859497 16.390991 -2.0278015c0.98913574 -0.18551636 1.8722534 -0.37402344 2.6405945 -0.5649414l0.5545349 -0.14355469"
+       fill-rule="evenodd"
+       id="path927" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m393.02893 350.56067l-0.46139526 1.5220032l2.1943665 -2.4487l-3.2549744 0.4653015z"
+       fill-rule="evenodd"
+       id="path929" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.78412 359.65433c0 44.519684 28.614166 89.0394 57.228333 89.0394"
+       fill-rule="evenodd"
+       id="path931" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.7841 359.65433c0 22.259827 7.1535645 44.519684 17.88388 61.21457c5.3651733 8.347443 11.624512 15.30365 18.330963 20.172974c3.3532104 2.4346619 6.818207 4.3476562 10.339111 5.6519165c1.760437 0.6521301 3.534851 1.1521301 5.316223 1.4890747c0.44540405 0.084228516 0.8911743 0.15826416 1.3372803 0.22195435l0.60028076 0.0786438"
+       fill-rule="evenodd"
+       id="path933" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m380.59183 448.4835l-1.1914368 1.0534973l3.1529236 -0.9329529l-3.0149841 -1.3119812z"
+       fill-rule="evenodd"
+       id="path935" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.3904 229.5441c22.905518 0 34.358246 -5.2125854 45.811005 -10.425186c11.452759 -5.2126007 22.905518 -10.425201 45.811035 -10.425201"
+       fill-rule="evenodd"
+       id="path937" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.3904 229.54411c22.905518 0 34.358276 -5.2126007 45.811035 -10.425201c5.7263794 -2.606308 11.452759 -5.2126007 18.610748 -7.167328c3.5789795 -0.97735596 7.515869 -1.7918243 11.989563 -2.3619537c2.2368774 -0.2850647 4.607971 -0.50904846 7.13562 -0.6617737c1.263794 -0.07633972 2.5668335 -0.13487244 3.9116821 -0.17433167l0.73657227 -0.018814087"
+       fill-rule="evenodd"
+       id="path939" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58563 208.7347l-1.111084 1.1379547l3.0761108 -1.1614532l-3.1029663 -1.0875549z"
+       fill-rule="evenodd"
+       id="path941" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905518 -39.425186 41.811005 -52.566925c20.905548 -13.141724 41.811066 -26.283463 41.811066 -52.566925"
+       fill-rule="evenodd"
+       id="path943" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905487 -39.4252 41.811005 -52.566925c10.452759 -6.570862 20.905548 -13.141739 28.745087 -21.355316c3.9197998 -4.1067963 7.1862793 -8.624268 9.472839 -13.757751c1.1432495 -2.566742 2.041504 -5.287491 2.6539917 -8.187912c0.30621338 -1.4502106 0.5410156 -2.945343 0.69921875 -4.4886017c0.03955078 -0.38581085 0.07434082 -0.7746353 0.10424805 -1.1665115l0.013000488 -0.18595123"
+       fill-rule="evenodd"
+       id="path945" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m535.90155 124.26837l1.0835571 1.1641159l-1.0132446 -3.1280365l-1.234436 3.0475311z"
+       fill-rule="evenodd"
+       id="path947" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m454.75735 231.661c22.314941 0 33.472443 20.960617 44.629913 41.92125c11.157471 20.960632 22.314941 41.921265 44.629944 41.921265"
+       fill-rule="evenodd"
+       id="path949" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m454.75735 231.66098c22.314941 0 33.472412 20.960632 44.629913 41.921265c5.5787354 10.480316 11.157471 20.960632 18.13092 28.820862c3.4866943 3.9301147 7.3220825 7.2052307 11.680481 9.497803c2.1791992 1.1462708 4.4891357 2.046936 6.95166 2.6610107c1.2312012 0.30703735 2.5006104 0.54241943 3.810852 0.7010803c0.16375732 0.019836426 0.32818604 0.03845215 0.49328613 0.055877686l0.13989258 0.013671875"
+       fill-rule="evenodd"
+       id="path951" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.59436 315.33255l-1.1792603 1.0671082l3.1420288 -0.9690857l-3.0298462 -1.2772827z"
+       fill-rule="evenodd"
+       id="path953" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.78412 455.65433c24.307068 0 36.4606 -3.7401428 48.614166 -7.480316c12.153534 -3.7401428 24.307098 -7.4802856 48.614166 -7.4802856"
+       fill-rule="evenodd"
+       id="path955" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m446.78412 455.65433c24.307098 0 36.460632 -3.7401733 48.614166 -7.480316c6.076782 -1.8700867 12.153564 -3.7401733 19.749542 -5.1427307c3.7979736 -0.7012634 7.975708 -1.285675 12.723206 -1.6947632c2.3737793 -0.20455933 4.8898926 -0.36523438 7.5722656 -0.47479248c1.3411255 -0.05480957 2.723816 -0.09680176 4.151062 -0.12512207l0.99108887 -0.017120361"
+       fill-rule="evenodd"
+       id="path957" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58545 440.71948l-1.1160889 1.1329956l3.0812378 -1.1477966l-3.0981445 -1.1012878z"
+       fill-rule="evenodd"
+       id="path959" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-68.0 10.0 -136.0 52.5 -136.0 104.99999"
+       fill-rule="evenodd"
+       id="path961" />
+    <path
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-34.0 5.0 -68.0 18.125 -93.5 36.562492c-12.75 9.21875 -23.375 19.765625 -30.8125 31.289062c-3.71875 5.7617188 -6.640625 11.767586 -8.6328125 17.973633c-0.99606323 3.1030273 -1.7597351 6.2561035 -2.274414 9.453735c-0.25732422 1.5988159 -0.45239258 3.2087708 -0.5831299 4.829178c-0.032684326 0.4051056 -0.061340332 0.81085205 -0.0859375 1.2172546l-0.013824463 0.24894714"
+       fill-rule="evenodd"
+       id="path963" />
+    <path
+       fill="#980000"
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m301.8311 146.25957l-1.0921936 -1.1560669l1.0363464 3.1204681l1.2119141 -3.0565796z"
+       fill-rule="evenodd"
+       id="path965" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m628.3904 213.5441c24.905518 0 37.358276 -1.2125854 49.811035 -2.4251862c12.452759 -1.2126007 24.905518 -2.4252014 49.811035 -2.4252014"
+       fill-rule="evenodd"
+       id="path967" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m628.39044 213.54411c24.905518 0 37.358215 -1.2126007 49.810974 -2.4252014c6.2263794 -0.606308 12.452759 -1.2126007 20.235779 -1.6673279c3.8914795 -0.22735596 8.172058 -0.41682434 13.036499 -0.54945374c2.432129 -0.0663147 5.010254 -0.11842346 7.758606 -0.15393066c1.3741455 -0.01777649 2.7908936 -0.031402588 4.253235 -0.04055786l1.0998535 -0.0051574707"
+       fill-rule="evenodd"
+       id="path969" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m724.5854 208.7025l-1.1217041 1.1274567l3.086914 -1.1324921l-3.0926514 -1.1166687z"
+       fill-rule="evenodd"
+       id="path971" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m622.7841 431.67007c0 -45.75589 36.58264 -68.63385 73.165344 -91.51181c36.582703 -22.87793 73.165344 -45.75589 73.165344 -91.511795"
+       fill-rule="evenodd"
+       id="path973" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m622.7841 431.67007c0 -45.75592 36.582703 -68.63385 73.165344 -91.51178c18.29132 -11.438995 36.582703 -22.87796 50.30121 -37.176697c6.859253 -7.149353 12.575256 -15.013641 16.576538 -23.950348c2.0005493 -4.4683533 3.5724487 -9.204803 4.644226 -14.254028c0.5359497 -2.5246277 0.94677734 -5.1274414 1.2236328 -7.814026c0.13842773 -1.3433075 0.24334717 -2.70755 0.3137207 -4.093445l0.035339355 -0.7969208"
+       fill-rule="evenodd"
+       id="path975" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m769.04407 252.07283l1.1011353 1.1475525l-1.0605469 -3.11232l-1.1881104 3.0658875z"
+       fill-rule="evenodd"
+       id="path977" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.7573 117.00678c36.88977 0 55.334656 13.614174 73.77954 27.22834c18.444885 13.6141815 36.88977 27.228348 73.77954 27.228348"
+       fill-rule="evenodd"
+       id="path979" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m598.7573 117.006775c36.88977 0 55.334656 13.6141815 73.77954 27.228348c9.222473 6.807083 18.444946 13.6141815 29.972961 18.719482c5.764038 2.552658 12.104431 4.679886 19.30951 6.16893c3.602478 0.7445221 7.4211426 1.3295135 11.492004 1.728363c2.0354614 0.19943237 4.13385 0.35231018 6.2998657 0.45535278c0.5415039 0.025772095 1.0872803 0.04840088 1.6373291 0.06788635c0.2750244 0.009719849 0.5510864 0.018676758 0.8282471 0.026824951l0.8128052 0.021453857"
+       fill-rule="evenodd"
+       id="path981" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m742.8896 171.42342l-1.1376343 1.1113739l3.1026611 -1.0884094l-3.076416 -1.160614z"
+       fill-rule="evenodd"
+       id="path983" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m451.69748 115.27639c21.078735 0 31.618134 -0.645668 42.1575 -1.2913437c10.539368 -0.645668 21.078735 -1.291336 42.15747 -1.291336"
+       fill-rule="evenodd"
+       id="path985" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m451.6975 115.27639c21.078735 0 31.618103 -0.645668 42.15747 -1.291336c5.269684 -0.32283783 10.539368 -0.645668 17.126495 -0.8877945c3.2935486 -0.12106323 6.9164734 -0.22195435 11.033356 -0.29257202c2.0584717 -0.035308838 4.2404785 -0.063056946 6.5665894 -0.081970215c1.1630249 -0.009460449 2.3620605 -0.016708374 3.5997314 -0.021591187l0.40423584 -7.9345703E-4"
+       fill-rule="evenodd"
+       id="path987" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m532.5854 112.70034l-1.1224365 1.1267548l3.0876465 -1.1305542l-3.09198 -1.1186066z"
+       fill-rule="evenodd"
+       id="path989" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m604.3975 309.543c67.82678 0 135.6535 -36.299225 135.6535 -72.59842"
+       fill-rule="evenodd"
+       id="path991" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m604.3975 309.543c33.91339 0 67.82678 -9.074799 93.26178 -22.687012c12.717529 -6.8060913 23.31543 -14.7465515 30.734009 -23.25418c3.7092896 -4.2538147 6.623657 -8.649414 8.610779 -13.115921c0.9935913 -2.233261 1.7553711 -4.4842224 2.2686157 -6.7440643c0.12835693 -0.56495667 0.2411499 -1.1304779 0.3381958 -1.6963959c0.04852295 -0.28297424 0.09307861 -0.5660553 0.13366699 -0.84921265c0.020263672 -0.14157104 0.039611816 -0.28315735 0.057922363 -0.42478943l0.049560547 -0.4055481"
+       fill-rule="evenodd"
+       id="path993" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m739.852 240.36588l1.0574341 1.1879883l-0.94329834 -3.1498566l-1.302124 3.0192566z"
+       fill-rule="evenodd"
+       id="path995" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m49.937008 5.086614l504.22046 0l0 27.464567l-504.22046 0z"
+       fill-rule="evenodd"
+       id="path997" />
+    <path
+       fill="#000000"
+       d="m60.296383 32.00661l0 -13.359373l1.78125 0l0 11.78125l6.562496 0l0 1.5781231l-8.343746 0zm10.250713 -11.468748l0 -1.890625l1.640625 0l0 1.890625l-1.640625 0zm0 11.468748l0 -9.671873l1.640625 0l0 9.671873l-1.640625 0zm4.144821 0l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm10.375717 0l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm18.089554 -1.4687481l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -
 1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051788 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.078125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000717 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.437
 5l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324654 5.765623l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm9.766342 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.296
 8731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm15.563217 4.843748l0 -1.2187481q-0.90625 1.4374981 -2.703125 1.4374981q-1.15625 0 -2.125 -0.6406231q-0.96875 -0.640625 -1.5 -1.78125q-0.53125 -1.140625 -0.53125 -2.625q0 -1.453125 0.484375 -2.625q0.484375 -1.1875 1.4375 -1.8125q0.96875 -0.625 2.171875 -0.625q0.875 0 1.546875 0.375q0.6875 0.359375 1.109375 0.953125l0 -4.796875l1.640625 0l0 13.359373l-1.53125 0zm-5.171875 -4.828123q0 1.859375 0.78125 2.78125q0.78125 0.921875 1.84375 0.921875q1.078125 0 1.828125 -0.875q0.75 -0.890625 0.75 -2.6875q0 -1.984375 -0.765625 -2.90625q-0.765625 -0.9375 -1.890625 -0.9375q-1.078125 0 -1.8125 0.890625q-0.734375 0.890625 -0.734375 2.8125zm15.906967 1.71875l1.6875 0.203125q-0.40625 1.484375 -1.
 484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm8.485092 2.875l1.625 -0.25q0.125 0.96875 0.75 1.5q0.625 0.515625 1.75 0.515625q1.125 0 1.671875 -0.453125q0.546875 -0.46875 0.546875 -1.09375q0 -0.546875 -0.484375 -0.875q-0.328125 -0.21875 -1.671875 -0.546875q-1.8125 -0.46875 -2.515625 -0.796875q-0.6875 -0.328125 -1.046875 -0.90625q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.640625 0.296875 -1.1875q0.296875 -0.5625 0.8125 -0.921875q0.375 -0.28125 1.03125 -0.46875q0.671
 875 -0.203125 1.421875 -0.203125q1.140625 0 2.0 0.328125q0.859375 0.328125 1.265625 0.890625q0.421875 0.5625 0.578125 1.5l-1.609375 0.21875q-0.109375 -0.75 -0.640625 -1.171875q-0.515625 -0.421875 -1.46875 -0.421875q-1.140625 0 -1.625 0.375q-0.46875 0.375 -0.46875 0.875q0 0.3125 0.1875 0.578125q0.203125 0.265625 0.640625 0.4375q0.234375 0.09375 1.4375 0.421875q1.75 0.453125 2.4375 0.75q0.6875 0.296875 1.078125 0.859375q0.390625 0.5625 0.390625 1.40625q0 0.828125 -0.484375 1.546875q-0.46875 0.71875 -1.375 1.125q-0.90625 0.3906231 -2.046875 0.3906231q-1.875 0 -2.875 -0.7812481q-0.984375 -0.78125 -1.25 -2.328125zm18.745804 1.421875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.2
 03125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm0.99580383 -3.375q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm20.793396 1.296875l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0
 .96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.0 3.546873l0 -9.671873l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.062498l-1.625 0zm12.853302 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.
 484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm15.453842 4.578125q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.421875q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.4
 0625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm7.781967 3.390625l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm8.230179 -1.640625l1.6874847 0.203125q-0.40625 1.484375 -1.4843597 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.6
 71875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.1562347 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.2187347 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm17.902756 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051941 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.07
 8125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000702 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324646 9.468748l0 -13.374998l1.484375 0l0 1
 .25q0.53125 -0.734375 1.1875 -1.09375q0.671875 -0.375 1.625 -0.375q1.234375 0 2.171875 0.640625q0.953125 0.625 1.4375 1.796875q0.484375 1.15625 0.484375 2.546875q0 1.484375 -0.53125 2.671875q-0.53125 1.1875 -1.546875 1.828125q-1.015625 0.6249981 -2.140625 0.6249981q-0.8125 0 -1.46875 -0.3437481q-0.65625 -0.34375 -1.0625 -0.875l0 4.703123l-1.640625 0zm1.484375 -8.484373q0 1.859375 0.75 2.765625q0.765625 0.890625 1.828125 0.890625q1.09375 0 1.875 -0.921875q0.78125 -0.9375 0.78125 -2.875q0 -1.84375 -0.765625 -2.765625q-0.75 -0.921875 -1.8125 -0.921875q-1.046875 0 -1.859375 0.984375q-0.796875 0.96875 -0.796875 2.84375zm15.203857 3.59375q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.42187
 5q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.40625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm10.516357 1.3125l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3
 .71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.015625 3.546873l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm15.953125 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.01562
 5 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm12.719482 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.179077 1.4687481l0 -8.406248l-1.453125 0l0 -1.265625l1.453125 0l0 -1.03125q0 -0.96875 0.171875 -1.453125q0.234375 -0.640625 0.828125 -1.03125q0.59375 -0.390625 1.671875 -0.390625q0.6875 0 1.53125 0.15625l-0.25 1.4375q-0.5 -0.09375 -0.953125 -0.09375q-0.75 0 -1.0625 0.328125q-0.3125 0.3125 -0.3125 1.1875l0 0.8906
 25l1.890625 0l0 1.265625l-1.890625 0l0 8.406248l-1.625 0zm4.7457886 0l0 -13.359373l1.640625 0l0 13.359373l-1.640625 0zm3.5823364 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm11.078857 4.843748l-2.96875 -9.671873l1.703125 0l1.53125 5.578125l0.578125 2.078125q0.046875 -0.15625 0.5 -2.0l1.546875 -5.65625l1.6875 0l1.4375 5.609375l0.484375 1.84375l0.5625 -1.859375l1.65625 -5.59375l1.59375 0l-3.03125 9.671873l-1.703125 0l-1.53125 -5.796873l-0.375 -1.640625l-1.953125 7.437498l-1.71
 875 0z"
+       fill-rule="nonzero"
+       id="path999" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index fb250abf5..a98b16f0e 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -57,6 +57,7 @@ Programmer's Guide
     metrics_lib
     bpf_lib
     ipsec_lib
+    graph_lib
     source_org
     dev_kit_build_system
     dev_kit_root_make_help
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index 000bbf501..d208547ec 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -62,6 +62,30 @@ New Features
 
   * Added support for matching on IPv4 Time To Live and IPv6 Hop Limit.
 
+* **Added rte_graph library.**
+
+  Graph architecture abstracts the data processing functions as a ``node`` and
+  ``links`` them together to create a complex ``graph`` to enable reusable/modular
+  data processing functions. The graph library provides API to enable graph
+  framework operations such as create, lookup, dump and destroy on graph and node
+  operations such as clone, edge update, and edge shrink, etc.
+  The API also allows to create the stats cluster to monitor per graph and per node stats.
+
+* **Added rte_node library which consists of a set of packet processing nodes.**
+
+  The rte_node library that consists of nodes used by rte_graph library. Each
+  node performs a specific packet processing function based on application
+  configuration. The following nodes are added:
+
+  * Null node: Skeleton node that defines the general structure of a node.
+  * Ethernet device node: Consists of ethernet Rx/Tx nodes as well as ethernet
+    control APIs.
+  * IPv4 lookup node: Consists of ipv4 extract and lpm lookup node. Routes can
+    be configured by the application through ``rte_node_ip4_route_add`` function.
+  * IPv4 rewrite node: Consists of ipv4 and ethernet header rewrite functionality
+    that can be configured through ``rte_node_ip4_rewrite_add`` function.
+  * Packet drop node: Frees the packets received to their respective mempool.
+
 
 Removed Items
 -------------
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v2 28/28] doc: add l3fwd graph application user guide
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (26 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 27/28] doc: add graph library programmer's guide guide jerinj
@ 2020-03-26 16:56   ` jerinj
  2020-03-27  6:49   ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem Jerin Jacob
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-26 16:56 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic, Ori Kam,
	Bruce Richardson, Radu Nicolau, Akhil Goyal, Tomasz Kantecki,
	Sunil Kumar Kori, Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Adding the user guide for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                                   |   1 +
 doc/guides/rel_notes/release_20_05.rst        |   8 +
 doc/guides/sample_app_ug/index.rst            |   1 +
 doc/guides/sample_app_ug/intro.rst            |   4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst | 327 ++++++++++++++++++
 5 files changed, 341 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 1d2cf6caa..e8a87e119 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1583,6 +1583,7 @@ F: doc/guides/sample_app_ug/l3_forward.rst
 
 M: Nithin Dabilpuram <ndabilpuram@marvell.com>
 F: examples/l3fwd-graph/
+F: doc/guides/sample_app_ug/l3_forward_graph.rst
 
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index d208547ec..9d3e7bd69 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -86,6 +86,14 @@ New Features
     that can be configured through ``rte_node_ip4_rewrite_add`` function.
   * Packet drop node: Frees the packets received to their respective mempool.
 
+* **Added new l3fwd-graph sample application.**
+
+  Added an example application ``l3fwd-graph``. It demonstrates the usage of graph
+  library and node library for packet processing. In addition to the library usage
+  demonstration, this application can use for performance comparison with existing
+  ``l3fwd`` (The static code without any nodes) with the modular ``l3fwd-graph``
+  approach.
+
 
 Removed Items
 -------------
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index ac3445147..cf9c1e44d 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -29,6 +29,7 @@ Sample Applications User Guides
     l2_forward_event
     l2_forward_cat
     l3_forward
+    l3_forward_graph
     l3_forward_power_man
     l3_forward_access_ctrl
     link_status_intr
diff --git a/doc/guides/sample_app_ug/intro.rst b/doc/guides/sample_app_ug/intro.rst
index 6cd0342a1..8ff223b16 100644
--- a/doc/guides/sample_app_ug/intro.rst
+++ b/doc/guides/sample_app_ug/intro.rst
@@ -54,6 +54,10 @@ examples are highlighted below.
   forwarding, or ``l3fwd`` application does forwarding based on Internet
   Protocol, IPv4 or IPv6 like a simple router.
 
+* :doc:`Network Layer 3 forwarding Graph<l3_forward_graph>`: The Network Layer3
+  forwarding Graph, or ``l3fwd_graph`` application does forwarding based on IPv4
+  like a simple router with DPDK Graph framework.
+
 * :doc:`Hardware packet copying<ioat>`: The Hardware packet copying,
   or ``ioatfwd`` application demonstrates how to use IOAT rawdev driver for
   copying packets between two threads.
diff --git a/doc/guides/sample_app_ug/l3_forward_graph.rst b/doc/guides/sample_app_ug/l3_forward_graph.rst
new file mode 100644
index 000000000..73153f82b
--- /dev/null
+++ b/doc/guides/sample_app_ug/l3_forward_graph.rst
@@ -0,0 +1,327 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+L3 Forwarding Graph Sample Application
+======================================
+
+The L3 Forwarding Graph application is a simple example of packet processing
+using the DPDK Graph framework. The application performs L3 forwarding using
+Graph framework and nodes written for graph framework.
+
+Overview
+--------
+
+The application demonstrates the use of the graph framework and graph nodes
+``ethdev_rx``, ``ip4_lookup``, ``ip4_rewrite``, ``ethdev_tx`` and ``pkt_drop`` in DPDK to
+implement packet forwarding.
+
+The initialization is very similar to those of the :doc:`l3_forward`.
+There is also additional initialization of graph for graph object creation
+and configuration per lcore.
+Run-time path is main thing that differs from L3 forwarding sample application.
+Difference is that forwarding logic starting from Rx, followed by LPM lookup,
+TTL update and finally Tx is implemented inside graph nodes. These nodes are
+interconnected in graph framework. Application main loop needs to walk over
+graph using ``rte_graph_walk()`` with graph objects created one per slave lcore.
+
+The lookup method is as per implementation of ``ip4_lookup`` graph node.
+The ID of the output interface for the input packet is the next hop returned by
+the LPM lookup. The set of LPM rules used by the application is statically
+configured and provided to ``ip4_lookup`` graph node and ``ip4_rewrite`` graph node
+using node control API ``rte_node_ip4_route_add()`` and ``rte_node_ip4_rewrite_add()``.
+
+In the sample application, only IPv4 forwarding is supported as of now.
+
+Compiling the Application
+-------------------------
+
+To compile the sample application see :doc:`compiling`.
+
+The application is located in the ``l3fwd-graph`` sub-directory.
+
+Running the Application
+-----------------------
+
+The application has a number of command line options similar to l3fwd::
+
+    ./l3fwd-graph [EAL options] -- -p PORTMASK
+                                   [-P]
+                                   --config(port,queue,lcore)[,(port,queue,lcore)]
+                                   [--eth-dest=X,MM:MM:MM:MM:MM:MM]
+                                   [--enable-jumbo [--max-pkt-len PKTLEN]]
+                                   [--no-numa]
+                                   [--per-port-pool]
+
+Where,
+
+* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
+
+* ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
+  Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
+
+* ``--config (port,queue,lcore)[,(port,queue,lcore)]:`` Determines which queues from which ports are mapped to which cores.
+
+* ``--eth-dest=X,MM:MM:MM:MM:MM:MM:`` Optional, ethernet destination for port X.
+
+* ``--enable-jumbo:`` Optional, enables jumbo frames.
+
+* ``--max-pkt-len:`` Optional, under the premise of enabling jumbo, maximum packet length in decimal (64-9600).
+
+* ``--no-numa:`` Optional, disables numa awareness.
+
+* ``--per-port-pool:`` Optional, set to use independent buffer pools per port. Without this option, single buffer pool is used for all ports.
+
+For example, consider a dual processor socket platform with 8 physical cores, where cores 0-7 and 16-23 appear on socket 0,
+while cores 8-15 and 24-31 appear on socket 1.
+
+To enable L3 forwarding between two ports, assuming that both ports are in the same socket, using two cores, cores 1 and 2,
+(which are in the same socket too), use the following command:
+
+.. code-block:: console
+
+    ./build/l3fwd-graph -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+
+In this command:
+
+*   The -l option enables cores 1, 2
+
+*   The -p option enables ports 0 and 1
+
+*   The --config option enables one queue on each port and maps each (port,queue) pair to a specific core.
+    The following table shows the mapping in this example:
+
++----------+-----------+-----------+-------------------------------------+
+| **Port** | **Queue** | **lcore** | **Description**                     |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 0        | 0         | 1         | Map queue 0 from port 0 to lcore 1. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 1        | 0         | 2         | Map queue 0 from port 1 to lcore 2. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+
+Refer to the *DPDK Getting Started Guide* for general information on running applications and
+the Environment Abstraction Layer (EAL) options.
+
+.. _l3_fwd_graph_explanation:
+
+Explanation
+-----------
+
+The following sections provide some explanation of the sample application code.
+As mentioned in the overview section, the initialization is similar to that of
+the :doc:`l3_forward`. Run-time path though similar in functionality to that of
+:doc:`l3_forward`, major part of the implementation is in graph nodes via used
+via ``librte_node`` library.
+The following sections describe aspects that are specific to the L3 Forwarding
+Graph sample application.
+
+Graph Node Pre-Init Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After device configuration and device Rx, Tx queue setup is complete,
+a minimal config of port id, num_rx_queues, num_tx_queues, mempools etc will
+be passed to *ethdev_** node ctrl API ``rte_node_eth_config()``. This will be
+lead to the clone of ``ethdev_rx`` and ``ethdev_tx`` nodes as ``ethdev_rx-X-Y`` and
+``ethdev_tx-X`` where X, Y represent port id and queue id associated with them.
+In case of ``ethdev_tx-X`` nodes, tx queue id assigned per instance of the node
+is same as graph id.
+
+These cloned nodes along with existing static nodes such as ``ip4_lookup`` and
+``ip4_rewrite`` will be used in graph creation to associate node's to lcore
+specific graph object.
+
+.. code-block:: c
+
+    RTE_ETH_FOREACH_DEV(portid)
+    {
+
+        /* ... */
+        ret = rte_eth_dev_configure(portid, nb_rx_queue,
+                                    n_tx_queue, &local_port_conf);
+        /* ... */
+
+        /* Init one TX queue per couple (lcore,port) */
+        queueid = 0;
+        for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+            /* ... */
+            ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+                                         socketid, txconf);
+            /* ... */
+            queueid++;
+        }
+
+        /* Setup ethdev node config */
+        ethdev_conf[nb_conf].port_id = portid;
+        ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+        ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+        if (!per_port_pool)
+            ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+        else
+          ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+        ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+        nb_conf++;
+        printf("\n");
+    }
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* Init RX queues */
+        for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+            /* ... */
+            if (!per_port_pool)
+                ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                             &rxq_conf, pktmbuf_pool[0][socketid]);
+            else
+              ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                           &rxq_conf, pktmbuf_pool[portid][socketid]);
+            /* ... */
+        }
+    }
+
+    /* Ethdev node config, skip rx queue mapping */
+    ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+
+Graph Initialization
+~~~~~~~~~~~~~~~~~~~~
+
+Now a graph needs to be created with a specific set of nodes for every lcore.
+A graph object returned after graph creation is a per lcore object and
+cannot be shared between lcores. Since ``ethdev_tx-X`` node is per port node,
+it can be associated with all the graphs created as all the lcores should have
+Tx capability for every port. But ``ethdev_rx-X-Y`` node is created per
+(port, rx_queue_id), so they should be associated with a graph based on
+the application argument ``--config`` specifying rx queue mapping to lcore.
+
+.. note::
+
+    The Graph creation will fail if the passed set of shell node pattern's
+    are not sufficient to meet their inter-dependency or even one node is not
+    found with a given regex node pattern.
+
+.. code-block:: c
+
+    static const char *node_patterns[] = {
+        "ip4*",
+        "ethdev_tx-*",
+        "pkt_drop",
+    };
+    uint16_t nb_patterns = RTE_DIM(node_patterns);
+
+    /* ... */
+
+    /* Create a graph object per lcore with common nodes and
+     * lcore specific nodes based on application arguments
+     */
+    memset(&graph_conf, 0, sizeof(graph_conf));
+
+    /* Common set of nodes in every lcore's graph object */
+    graph_conf.node_patterns = node_patterns;
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* ... */
+
+        /* Skip graph creation if no source exists */
+        if (!qconf->n_rx_queue)
+            continue;
+
+        /* Add rx node patterns of this lcore based on --config */
+        for (i = 0; i < qconf->n_rx_queue; i++) {
+            graph_conf.node_patterns[nb_patterns + i] =
+                                qconf->rx_queue_list[i].node_name;
+        }
+
+        graph_conf.nb_node_patterns = nb_patterns + i;
+        graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+        snprintf(qconf->name, sizeof(qconf->name), "worker_%u", lcore_id);
+
+        graph_id = rte_graph_create(qconf->name, &graph_conf);
+
+        /* ... */
+
+        qconf->graph = rte_graph_lookup(qconf->name);
+
+        /* ... */
+    }
+
+Forwarding data(Route, Next-Hop) addition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once graph objects are created, node specific info like routes and rewrite
+headers will be provided run-time using ``rte_node_ip4_route_add()`` and
+``rte_node_ip4_rewrite_add()`` API.
+
+.. note::
+
+    Since currently ``ip4_lookup`` and ``ip4_rewrite`` nodes don't support
+    lock-less mechanisms(RCU, etc) to add run-time forwarding data like route and
+    rewrite data, forwarding data is added before packet processing loop is
+    launched on slave lcore.
+
+.. code-block:: c
+
+    /* Add route to ip4 graph infra */
+    for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+        /* ... */
+
+        dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+        next_hop = i;
+
+        /* ... */
+        ret = rte_node_ip4_route_add(ipv4_l3fwd_lpm_route_array[i].ip,
+                                     ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+                                     RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+        /* ... */
+
+        memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+        /* Add next hop for a given destination */
+        ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+                                       rewrite_len, dst_port);
+
+        RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+                route_str, next_hop);
+    }
+
+Packet Forwarding using Graph Walk
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that all the device configurations are done, graph creations are done and
+forwarding data is updated with nodes, slave lcores will be launched with graph
+main loop. Graph main loop is very simple in the sense that it needs to
+continuously call a non-blocking API ``rte_graph_walk()`` with it's lcore
+specific graph object that was already created.
+
+.. note::
+
+    rte_graph_walk() will walk over all the sources nodes i.e ``ethdev_rx-X-Y``
+    associated with a given graph and Rx the available packets and enqueue them
+    to the following node ``ip4_lookup`` which then will enqueue them to ``ip4_rewrite``
+    node if LPM lookup succeeds. ``ip4_rewrite`` node then will update Ethernet header
+    as per next-hop data and transmit the packet via port 'Z' by enqueuing
+    to ``ethdev_tx-Z`` node instance in its graph object.
+
+.. code-block:: c
+
+    /* Main processing loop */
+    static int
+    graph_main_loop(void *conf)
+    {
+        // ...
+
+        lcore_id = rte_lcore_id();
+        qconf = &lcore_conf[lcore_id];
+        graph = qconf->graph;
+
+        RTE_LOG(INFO, L3FWD_GRAPH,
+                "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+                qconf->name, graph);
+
+        /* Walk over graph until signal to quit */
+        while (likely(!force_quit))
+            rte_graph_walk(graph);
+        return 0;
+    }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (27 preceding siblings ...)
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 28/28] doc: add l3fwd graph application user guide jerinj
@ 2020-03-27  6:49   ` Jerin Jacob
  2020-03-27 10:42     ` Thomas Monjalon
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
  29 siblings, 1 reply; 219+ messages in thread
From: Jerin Jacob @ 2020-03-27  6:49 UTC (permalink / raw)
  To: Jerin Jacob
  Cc: dpdk-dev, Thomas Monjalon, David Marchand, Ray Kinsella,
	Mattias Rönnblom, Kiran Kumar K, Pavan Nikhilesh,
	Nithin Dabilpuram

On Thu, Mar 26, 2020 at 10:26 PM <jerinj@marvell.com> wrote:
>
> From: Jerin Jacob <jerinj@marvell.com>
>
> Using graph traversal for packet processing is a proven architecture
> that has been implemented in various open source libraries.
>
> Graph architecture for packet processing enables abstracting the data
> processing functions as “nodes” and “links” them together to create a
> complex “graph” to create reusable/modular data processing functions.
>
> The patchset further includes performance enhancements and modularity
> to the DPDK as discussed in more detail below.
>
> v2..v1:
> ------
> 1) Added programmer guide/implementation documentation and l3fwd-graph doc
>
> Hosted in netlify for easy reference:
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In case if someone interested to know the steps to host DPDK
documentation for free in netlify.

1) Create a DPDK repo with your code(documentation files) in GitHub

2) Open https://app.netlify.com/ URL

3) Sign in  netlify with GitHub account

4) Hosting procedure in netlify:
         a) Select "New site from git"
         b) Select the DPDK repo in Github
         c) Configure the following Build settings and deploy:
                  i) Repository: < Point to Selected DPDK repo in Github>
                 ii) Base directory: Not set
                  iii) Build command: pip install sphinx && pip
install sphinx_rtd_theme && make doc-api-html && make doc-guides-html
                  iv) Publish directory: build
d) Update the "Site details" if needed like Site name. The final site
will be "your site name".netlify.com/,

Once it is deployed,

1) Guide URL will be at: "your site name".netlify.com/doc/html/guides/

Example: https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html

2) API doc URL will be at: "your site name".netlify.com/doc/html/api/

example: https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html

> Programmer’s Guide:
> https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html
>
> l3fwd-graph doc:
> https://dpdk-graph.netlify.com/doc/html/guides/sample_app_ug/l3_forward_graph.html
>
> API doc:
> https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html
> https://dpdk-graph.netlify.com/doc/html/api/rte__graph__worker_8h.html
> https://dpdk-graph.netlify.com/doc/html/api/rte__node__eth__api_8h.html
> https://dpdk-graph.netlify.com/doc/html/api/rte__node__ip4__api_8h.html
>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v2 20/28] node: ipv4 lookup for x86
  2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 20/28] node: ipv4 lookup for x86 jerinj
@ 2020-03-27  8:40     ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-03-27  8:40 UTC (permalink / raw)
  To: Jerin Jacob
  Cc: Nithin Dabilpuram, Pavan Nikhilesh, dpdk-dev, Thomas Monjalon,
	David Marchand, Ray Kinsella, Mattias Rönnblom,
	Kiran Kumar K

On Thu, Mar 26, 2020 at 10:30 PM <jerinj@marvell.com> wrote:
>
> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
>
> Add IPv4 lookup process function for ip4_lookup
> rte_node. This node performs LPM lookup using x86_64
> vector supported RTE_LPM API on every packet received
> and forwards it to a next node that is identified by
> lookup result.
>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>

>  }
>
> +#elif defined(RTE_ARCH_X86)

# Could you move the below code to ipv4_looup_sse.h and arm64 code to
ipv4_looup_neon.h
# In order to make this node work for PPC and ARMv7, please add a
scalar version of ip4_lookup_node_process() too

> +
> +/* X86 SSE */
> +static uint16_t
> +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> +                       void **objs, uint16_t nb_objs)
> +{
> +       struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
> +       rte_edge_t next0, next1, next2, next3, next_index;
> +       struct rte_ipv4_hdr *ipv4_hdr;
> +       uint32_t ip0, ip1, ip2, ip3;
> +       void **to_next, **from;
> +       uint16_t last_spec = 0;
> +       uint16_t n_left_from;
> +       struct rte_lpm *lpm;
> +       uint16_t held = 0;
> +       uint32_t drop_nh;
> +       rte_xmm_t dst;
> +       __m128i dip; /* SSE register */
> +       int rc, i;

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem
  2020-03-27  6:49   ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem Jerin Jacob
@ 2020-03-27 10:42     ` Thomas Monjalon
  0 siblings, 0 replies; 219+ messages in thread
From: Thomas Monjalon @ 2020-03-27 10:42 UTC (permalink / raw)
  To: Jerin Jacob, Jerin Jacob
  Cc: dpdk-dev, David Marchand, Ray Kinsella, Mattias Rönnblom,
	Kiran Kumar K, Pavan Nikhilesh, Nithin Dabilpuram

27/03/2020 07:49, Jerin Jacob:
> In case if someone interested to know the steps to host DPDK
> documentation for free in netlify.
> 
> 1) Create a DPDK repo with your code(documentation files) in GitHub
> 
> 2) Open https://app.netlify.com/ URL
> 
> 3) Sign in  netlify with GitHub account
> 
> 4) Hosting procedure in netlify:
>          a) Select "New site from git"
>          b) Select the DPDK repo in Github
>          c) Configure the following Build settings and deploy:
>                   i) Repository: < Point to Selected DPDK repo in Github>
>                  ii) Base directory: Not set
>                   iii) Build command: pip install sphinx && pip
> install sphinx_rtd_theme && make doc-api-html && make doc-guides-html
>                   iv) Publish directory: build
> d) Update the "Site details" if needed like Site name. The final site
> will be "your site name".netlify.com/,
> 
> Once it is deployed,
> 
> 1) Guide URL will be at: "your site name".netlify.com/doc/html/guides/
> 
> Example: https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html
> 
> 2) API doc URL will be at: "your site name".netlify.com/doc/html/api/
> 
> example: https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html

Thanks, that looks convenient and very helpful!




^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 00/29] graph: introduce graph subsystem
  2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
                     ` (28 preceding siblings ...)
  2020-03-27  6:49   ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem Jerin Jacob
@ 2020-03-31 19:29   ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support jerinj
                       ` (29 more replies)
  29 siblings, 30 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Using graph traversal for packet processing is a proven architecture
that has been implemented in various open source libraries.

Graph architecture for packet processing enables abstracting the data
processing functions as “nodes” and “links” them together to create a
complex “graph” to create reusable/modular data processing functions. 

The patchset further includes performance enhancements and modularity
to the DPDK as discussed in more detail below.

v3..v2:
-------
1) refactor ipv4 node lookup by moving SSE and NEON specific code to
lib/librte_node/ip4_lookup_sse.h and lib/librte_node/ip4_lookup_neon.h
2) Add scalar version of process() function for ipv4 lookup to make
the node work on NON x86 and arm64 machines.

v2..v1:
------
1) Added programmer guide/implementation documentation and l3fwd-graph doc

Hosted in netlify for easy reference:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Programmer’s Guide:
https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html

l3fwd-graph doc:
https://dpdk-graph.netlify.com/doc/html/guides/sample_app_ug/l3_forward_graph.html

API doc:
https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__graph__worker_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__eth__api_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__ip4__api_8h.html

2) Added the release notes for the this feature

3) Fix build issues reported by CI for v1:
http://mails.dpdk.org/archives/test-report/2020-March/121326.html

RFC..v1:
--------

1) Split the patch to more logical ones for review.
2) Added doxygen comments for the API
3) Code cleanup
4) Additional performance improvements.
Delta between l3fwd and l3fwd-graph is negligible now.
(~1%) on octeontx2.
5) Added SIMD routines for x86 in additional to arm64.

Addional nodes planned for v20.08
----------------------------------
1) Packet classification node
2) Support for IPV6 LPM node


This patchset contains
-----------------------------
1) The API definition to "create" nodes and "link" together to create a
"graph" for packet processing. See, lib/librte_graph/rte_graph.h  

2) The Fast path API definition for the graph walker and enqueue
function used by the workers. See, lib/librte_graph/rte_graph_worker.h

3) Optimized SW implementation for (1) and (2). See, lib/librte_graph/

4) Test case to verify the graph infrastructure functionality
See, app/test/test_graph.c
 
5) Performance test cases to evaluate the cost of graph walker and nodes
enqueue fast-path function for various combinations.

See app/test/test_graph_perf.c

6) Packet processing nodes(Null, Rx, Tx, Pkt drop, IPV4 rewrite, IPv4
lookup)
using graph infrastructure. See lib/librte_node/*

7) An example application to showcase l3fwd
(functionality same as existing examples/l3fwd) using graph
infrastructure and use packets processing nodes (item (6)). See examples/l3fwd-graph/.

Performance
-----------
1) Graph walk and node enqueue overhead can be tested with performance
test case application [1]
# If all packets go from a node to another node (we call it as
# "homerun") then it will be just a pointer swap for a burst of packets.
# In the worst case, a couple of handful cycles to move an object from a
node to another node.

2) Performance comparison with existing l3fwd (The complete static code
with out any nodes) vs modular l3fwd-graph with 5 nodes
(ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
Here is graphical representation of the l3fwd-graph as Graphviz dot
file: 
http://bit.ly/39UPPGm

# l3fwd-graph performance is -1.2% wrt static l3fwd.

# We have simulated the similar test with existing librte_pipeline
# application [4].
ip_pipline application is -48.62% wrt static l3fwd.

The above results are on octeontx2. It may vary on other platforms.
The platforms with higher L1 and L2 caches will have further better
performance.


Tested architectures:
--------------------
1) AArch64
2) X86


Identified tweaking for better performance on different targets
---------------------------------------------------------------
1) Test with various burst size values (256, 128, 64, 32) using
CONFIG_RTE_GRAPH_BURST_SIZE config option.
Based on our testing, on x86 and arm64 servers, The sweet spot is 256
burst size.
While on arm64 embedded SoCs, it is either 64 or 128.

2) Disable node statistics (use CONFIG_RTE_LIBRTE_GRAPH_STATS config
option)
if not needed.

3) Use arm64 optimized memory copy for arm64 architecture by
selecting CONFIG_RTE_ARCH_ARM64_MEMCPY. 

Commands to run tests
---------------------

[1] 
perf test:
echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[2]
functionality test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[3]
l3fwd-graph:
./l3fwd-graph -c 0x100  -- -p 0x3 --config="(0, 0, 8)" -P

[4]
# ./ip_pipeline --c 0xff0000 -- -s route.cli

Route.cli: (Copy paste to the shell to avoid dos format issues)

https://pastebin.com/raw/B4Ktx7TT

Jerin Jacob (13):
  graph: define the public API for graph support
  graph: implement node registration
  graph: implement node operations
  graph: implement node debug routines
  graph: implement internal graph operation helpers
  graph: populate fastpath memory for graph reel
  graph: implement create and destroy APIs
  graph: implement graph operation APIs
  graph: implement Graphviz export
  graph: implement debug routines
  graph: implement stats support
  graph: implement fastpath API routines
  doc: add graph library programmer's guide guide

Kiran Kumar K (2):
  graph: add unit test case
  node: add ipv4 rewrite node

Nithin Dabilpuram (11):
  node: add log infra and null node
  node: add ethdev Rx node
  node: add ethdev Tx node
  node: add ethdev Rx and Tx node ctrl API
  node: ipv4 lookup for arm64
  node: add ipv4 rewrite and lookup ctrl API
  node: add packet drop node
  l3fwd-graph: add graph based l3fwd skeleton
  l3fwd-graph: add ethdev configuration changes
  l3fwd-graph: add graph config and main loop
  doc: add l3fwd graph application user guide

Pavan Nikhilesh (3):
  graph: add performance testcase
  node: add generic ipv4 lookup node
  node: ipv4 lookup for x86

 MAINTAINERS                                   |   14 +
 app/test/Makefile                             |    7 +
 app/test/meson.build                          |   12 +-
 app/test/test_graph.c                         |  819 ++++
 app/test/test_graph_perf.c                    | 1057 ++++++
 config/common_base                            |   12 +
 config/rte_config.h                           |    4 +
 doc/api/doxy-api-index.md                     |    5 +
 doc/api/doxy-api.conf.in                      |    2 +
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   32 +
 doc/guides/sample_app_ug/index.rst            |    1 +
 doc/guides/sample_app_ug/intro.rst            |    4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst |  327 ++
 examples/Makefile                             |    3 +
 examples/l3fwd-graph/Makefile                 |   58 +
 examples/l3fwd-graph/main.c                   | 1111 ++++++
 examples/l3fwd-graph/meson.build              |   13 +
 examples/meson.build                          |    6 +-
 lib/Makefile                                  |    6 +
 lib/librte_graph/Makefile                     |   28 +
 lib/librte_graph/graph.c                      |  589 +++
 lib/librte_graph/graph_debug.c                |   84 +
 lib/librte_graph/graph_ops.c                  |  169 +
 lib/librte_graph/graph_populate.c             |  234 ++
 lib/librte_graph/graph_private.h              |  347 ++
 lib/librte_graph/graph_stats.c                |  406 ++
 lib/librte_graph/meson.build                  |   11 +
 lib/librte_graph/node.c                       |  421 +++
 lib/librte_graph/rte_graph.h                  |  786 ++++
 lib/librte_graph/rte_graph_version.map        |   47 +
 lib/librte_graph/rte_graph_worker.h           |  542 +++
 lib/librte_node/Makefile                      |   32 +
 lib/librte_node/ethdev_ctrl.c                 |  116 +
 lib/librte_node/ethdev_rx.c                   |  221 ++
 lib/librte_node/ethdev_rx_priv.h              |   81 +
 lib/librte_node/ethdev_tx.c                   |   86 +
 lib/librte_node/ethdev_tx_priv.h              |   62 +
 lib/librte_node/ip4_lookup.c                  |  216 ++
 lib/librte_node/ip4_lookup_neon.h             |  238 ++
 lib/librte_node/ip4_lookup_sse.h              |  244 ++
 lib/librte_node/ip4_rewrite.c                 |  326 ++
 lib/librte_node/ip4_rewrite_priv.h            |   77 +
 lib/librte_node/log.c                         |   14 +
 lib/librte_node/meson.build                   |   10 +
 lib/librte_node/node_private.h                |   96 +
 lib/librte_node/null.c                        |   23 +
 lib/librte_node/pkt_drop.c                    |   26 +
 lib/librte_node/rte_node_eth_api.h            |   70 +
 lib/librte_node/rte_node_ip4_api.h            |   87 +
 lib/librte_node/rte_node_version.map          |    9 +
 lib/meson.build                               |    5 +-
 meson.build                                   |    1 +
 mk/rte.app.mk                                 |    2 +
 58 files changed, 14702 insertions(+), 5 deletions(-)
 create mode 100644 app/test/test_graph.c
 create mode 100644 app/test/test_graph_perf.c
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/graph_debug.c
 create mode 100644 lib/librte_graph/graph_ops.c
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/graph_stats.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/node.c
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map
 create mode 100644 lib/librte_graph/rte_graph_worker.h
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/ip4_lookup_neon.h
 create mode 100644 lib/librte_node/ip4_lookup_sse.h
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/pkt_drop.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h
 create mode 100644 lib/librte_node/rte_node_ip4_api.h
 create mode 100644 lib/librte_node/rte_node_version.map

-- 
2.25.1


^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-04-03  9:26       ` Wang, Xiao W
  2020-04-06 12:36       ` Andrzej Ostruszka
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 02/29] graph: implement node registration jerinj
                       ` (28 subsequent siblings)
  29 siblings, 2 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Thomas Monjalon, Bruce Richardson, John McNamara,
	Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, david.marchand, mdr, mattias.ronnblom, pbhagavatula, ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Graph architecture abstracts the data processing functions as
"node" and "link" them together to create a complex "graph" to enable
reusable/modular data processing functions.

These APIs enables graph framework operations such as create, lookup,
dump and destroy on graph and node operations such as clone,
edge update, and edge shrink, etc. The API also allows creating the
stats cluster to monitor per graph and per node stats.

This patch defines the public API for graph support.
This patch also adds support for the build infrastructure and
update the MAINTAINERS file for the graph subsystem.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                            |   5 +
 config/common_base                     |   7 +
 config/rte_config.h                    |   4 +
 doc/api/doxy-api-index.md              |   1 +
 doc/api/doxy-api.conf.in               |   1 +
 lib/Makefile                           |   3 +
 lib/librte_graph/Makefile              |  22 +
 lib/librte_graph/graph.c               |   5 +
 lib/librte_graph/meson.build           |  11 +
 lib/librte_graph/rte_graph.h           | 786 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   3 +
 lib/meson.build                        |   2 +-
 mk/rte.app.mk                          |   1 +
 13 files changed, 850 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index db235c2cc..bc7085983 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1469,6 +1469,11 @@ F: examples/bpf/
 F: app/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Graph - EXPERIMENTAL
+M: Jerin Jacob <jerinj@marvell.com>
+M: Kiran Kumar K <kirankumark@marvell.com>
+F: lib/librte_graph/
+
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index c31175f9d..32f982136 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1074,6 +1074,13 @@ CONFIG_RTE_LIBRTE_BPF_ELF=n
 #
 CONFIG_RTE_LIBRTE_IPSEC=y
 
+#
+# Compile librte_graph
+#
+CONFIG_RTE_LIBRTE_GRAPH=y
+CONFIG_RTE_GRAPH_BURST_SIZE=256
+CONFIG_RTE_LIBRTE_GRAPH_STATS=y
+
 #
 # Compile the test application
 #
diff --git a/config/rte_config.h b/config/rte_config.h
index d30786bc0..e9201fd46 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -98,6 +98,10 @@
 /* KNI defines */
 #define RTE_KNI_PREEMPT_DEFAULT 1
 
+/* rte_graph defines */
+#define RTE_GRAPH_BURST_SIZE 256
+#define RTE_LIBRTE_GRAPH_STATS 1
+
 /****** driver defines ********/
 
 /* QuickAssist device */
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index dff496be0..5cc50f750 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -159,6 +159,7 @@ The public API headers are grouped by topics:
   * [pipeline]         (@ref rte_pipeline.h)
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
+  * [graph]            (@ref rte_graph.h):
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 1c4392eec..759a7213e 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -33,6 +33,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_eventdev \
                           @TOPDIR@/lib/librte_fib \
                           @TOPDIR@/lib/librte_flow_classify \
+                          @TOPDIR@/lib/librte_graph \
                           @TOPDIR@/lib/librte_gro \
                           @TOPDIR@/lib/librte_gso \
                           @TOPDIR@/lib/librte_hash \
diff --git a/lib/Makefile b/lib/Makefile
index 46b91ae1a..1f572b659 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -119,6 +119,9 @@ DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
 DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu
 DEPDIRS-librte_rcu := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
+DEPDIRS-librte_graph := librte_eal
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
new file mode 100644
index 000000000..26fe514f3
--- /dev/null
+++ b/lib/librte_graph/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_graph.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal
+
+EXPORT_MAP := rte_graph_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
new file mode 100644
index 000000000..a55bf443a
--- /dev/null
+++ b/lib/librte_graph/graph.c
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "rte_graph.h"
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
new file mode 100644
index 000000000..455cf2ba5
--- /dev/null
+++ b/lib/librte_graph/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+name = 'graph'
+
+sources = files('graph.c')
+headers = files('rte_graph.h')
+allow_experimental_apis = true
+
+deps += ['eal']
diff --git a/lib/librte_graph/rte_graph.h b/lib/librte_graph/rte_graph.h
new file mode 100644
index 000000000..4bcf0a6e5
--- /dev/null
+++ b/lib/librte_graph/rte_graph.h
@@ -0,0 +1,786 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_H_
+#define _RTE_GRAPH_H_
+
+/**
+ * @file rte_graph.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph architecture abstracts the data processing functions as
+ * "node" and "link" them together to create a complex "graph" to enable
+ * reusable/modular data processing functions.
+ *
+ * This API enables graph framework operations such as create, lookup,
+ * dump and destroy on graph and node operations such as clone,
+ * edge update, and edge shrink, etc. The API also allows to create the stats
+ * cluster to monitor per graph and per node stats.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_GRAPH_NAMESIZE 64 /**< Max length of graph name. */
+#define RTE_NODE_NAMESIZE 64  /**< Max length of node name. */
+#define RTE_GRAPH_OFF_INVALID UINT32_MAX /**< Invalid graph offset. */
+#define RTE_NODE_ID_INVALID UINT32_MAX   /**< Invalid node id. */
+#define RTE_EDGE_ID_INVALID UINT16_MAX   /**< Invalid edge id. */
+#define RTE_GRAPH_ID_INVALID UINT16_MAX  /**< Invalid graph id. */
+#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL /**< Graph fence data. */
+
+typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
+typedef uint32_t rte_node_t;       /**< Node id type. */
+typedef uint16_t rte_edge_t;       /**< Edge id type. */
+typedef uint16_t rte_graph_t;      /**< Graph id type. */
+
+/** Burst size in terms of log2 */
+#if RTE_GRAPH_BURST_SIZE == 1
+#define RTE_GRAPH_BURST_SIZE_LOG2 0  /**< Object burst size of 1. */
+#elif RTE_GRAPH_BURST_SIZE == 2
+#define RTE_GRAPH_BURST_SIZE_LOG2 1  /**< Object burst size of 2. */
+#elif RTE_GRAPH_BURST_SIZE == 4
+#define RTE_GRAPH_BURST_SIZE_LOG2 2  /**< Object burst size of 4. */
+#elif RTE_GRAPH_BURST_SIZE == 8
+#define RTE_GRAPH_BURST_SIZE_LOG2 3  /**< Object burst size of 8. */
+#elif RTE_GRAPH_BURST_SIZE == 16
+#define RTE_GRAPH_BURST_SIZE_LOG2 4  /**< Object burst size of 16. */
+#elif RTE_GRAPH_BURST_SIZE == 32
+#define RTE_GRAPH_BURST_SIZE_LOG2 5  /**< Object burst size of 32. */
+#elif RTE_GRAPH_BURST_SIZE == 64
+#define RTE_GRAPH_BURST_SIZE_LOG2 6  /**< Object burst size of 64. */
+#elif RTE_GRAPH_BURST_SIZE == 128
+#define RTE_GRAPH_BURST_SIZE_LOG2 7  /**< Object burst size of 128. */
+#elif RTE_GRAPH_BURST_SIZE == 256
+#define RTE_GRAPH_BURST_SIZE_LOG2 8  /**< Object burst size of 256. */
+#else
+#error "Unsupported burst size"
+#endif
+
+/* Forward declaration */
+struct rte_node;  /**< Node data */
+struct rte_graph; /**< Graph data */
+struct rte_graph_cluster_stats;      /**< Stats for Cluster of graphs */
+struct rte_graph_cluster_node_stats; /**< Node stats within cluster of graphs */
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node process function.
+ *
+ * The function invoked when the worker thread walks on nodes using
+ * rte_graph_walk().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param objs
+ *   Pointer to an array of objects to be processed.
+ * @param nb_objs
+ *   Number of objects in the array.
+ *
+ * @return
+ *   Number of objects processed.
+ *
+ * @see rte_graph_walk()
+ *
+ */
+typedef uint16_t (*rte_node_process_t)(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node initialization function.
+ *
+ * The function invoked when the user creates the graph using rte_graph_create()
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ *
+ * @see rte_graph_create()
+ */
+typedef int (*rte_node_init_t)(const struct rte_graph *graph,
+			       struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node finalization function.
+ *
+ * The function invoked when the user destroys the graph using
+ * rte_graph_destroy().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @see rte_graph_destroy()
+ */
+typedef void (*rte_node_fini_t)(const struct rte_graph *graph,
+				struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph cluster stats callback.
+ *
+ * @param is_first
+ *   Flag to denote that stats are of the first node.
+ * @param is_last
+ *   Flag to denote that stats are of the last node.
+ * @param cookie
+ *   Cookie supplied during stats creation.
+ * @param stats
+ *   Node cluster stats data.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ */
+typedef int (*rte_graph_cluster_stats_cb_t)(bool is_first, bool is_last,
+	     void *cookie, const struct rte_graph_cluster_node_stats *stats);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for creating the graph.
+ *
+ * @see rte_graph_create()
+ */
+struct rte_graph_param {
+	int socket_id; /**< Socket id where memory is allocated. */
+	uint16_t nb_node_patterns;  /**< Number of node patterns. */
+	const char **node_patterns;
+	/**< Array of node patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for graph cluster stats create.
+ *
+ * @see rte_graph_cluster_stats_create()
+ */
+struct rte_graph_cluster_stats_param {
+	int socket_id;
+	/**< Socket id where memory is allocated */
+	rte_graph_cluster_stats_cb_t fn;
+	/**< Stats print callback function. NULL value allowed, in that case,
+	 *   default print stat function used.
+	 */
+	RTE_STD_C11
+	union {
+		void *cookie;
+		FILE *f; /**< File pointer to dump the stats when fn == NULL. */
+	};
+	uint16_t nb_graph_patterns;  /**< Number of graph patterns. */
+	const char **graph_patterns;
+	/**< Array of graph patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node cluster stats data structure.
+ *
+ * @see struct rte_graph_cluster_stats_param::fn
+ */
+struct rte_graph_cluster_node_stats {
+	uint64_t ts;	    /**< Current timestamp. */
+	uint64_t calls;	    /**< Current number of calls made. */
+	uint64_t objs;      /**< Current number of objs processed. */
+	uint64_t cycles;    /**< Current number of cycles. */
+
+	uint64_t prev_ts;	/**< Previous call timestamp. */
+	uint64_t prev_calls;	/**< Previous number of calls. */
+	uint64_t prev_objs;	/**< Previous number of processed objs. */
+	uint64_t prev_cycles;	/**< Previous number of cycles. */
+
+	uint64_t realloc_count; /**< Realloc count. */
+
+	rte_node_t id;	/**< Node identifier of stats. */
+	uint64_t hz;	/**< Cycles per seconds. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+} __rte_cache_aligned;
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure defines the node registration parameters.
+ *
+ * @see __rte_node_register(), RTE_NODE_REGISTER()
+ */
+struct rte_node_register {
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
+	rte_node_process_t process; /**< Node process function. */
+	rte_node_init_t init;       /**< Node init function. */
+	rte_node_fini_t fini;       /**< Node fini function. */
+	rte_node_t id;		    /**< Node Identifier. */
+	rte_node_t parent_id;       /**< Identifier of parent node. */
+	rte_edge_t nb_edges;        /**< Number of edges from this node. */
+	const char *next_nodes[];   /**< Names of next nodes. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create Graph.
+ *
+ * Create memory reel, detect loops and find isolated nodes.
+ *
+ * @param name
+ *   Unique name for this graph.
+ * @param prm
+ *   Graph parameter, includes node names and count to be included
+ *   in this graph.
+ *
+ * @return
+ *   Unique graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy Graph.
+ *
+ * Free Graph memory reel.
+ *
+ * @param name
+ *   Name of the graph to destroy.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_destroy(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph id from graph name.
+ *
+ * @param name
+ *   Name of the graph to get id.
+ *
+ * @return
+ *   Graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph name from graph id.
+ *
+ * @param id
+ *   id of the graph to get name.
+ *
+ * @return
+ *   Graph name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_graph_id_to_name(rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Export the graph as graph viz dot file
+ *
+ * @param name
+ *   Name of the graph to export.
+ * @param f
+ *   File pointer to export the graph.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_export(const char *name, FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph object from its name.
+ *
+ * Typical usage of this API to get graph objects in the worker thread and
+ * followed calling rte_graph_walk() in a loop.
+ *
+ * @param name
+ *   Name of the graph.
+ *
+ * @return
+ *   Graph pointer on success, NULL otherwise.
+ *
+ * @see rte_graph_walk()
+ */
+__rte_experimental
+struct rte_graph *rte_graph_lookup(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum number of graph available.
+ *
+ * @return
+ *   Maximum graph count on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump the graph information to file.
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param id
+ *   Graph id to get graph info.
+ */
+__rte_experimental
+void rte_graph_dump(FILE *f, rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all graphs information to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ */
+__rte_experimental
+void rte_graph_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump graph information along with node info to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param graph
+ *   Graph pointer to get graph info.
+ * @param all
+ *   true to dump nodes in the graph.
+ */
+__rte_experimental
+void rte_graph_obj_dump(FILE *f, struct rte_graph *graph, bool all);
+
+/** Macro to browse rte_node object after the graph creation */
+#define rte_graph_foreach_node(count, off, graph, node)                        \
+	for (count = 0, off = graph->nodes_start,                              \
+	     node = RTE_PTR_ADD(graph, off);                                   \
+	     count < graph->nb_nodes;                                          \
+	     off = node->next, node = RTE_PTR_ADD(graph, off), count++)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node object with in graph from id.
+ *
+ * @param graph_id
+ *   Graph id to get node pointer from.
+ * @param node_id
+ *   Node id to get node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get(rte_graph_t graph_id, uint32_t node_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node pointer with in graph from name.
+ *
+ * @param graph
+ *   Graph name to get node pointer from.
+ * @param name
+ *   Node name to get the node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get_by_name(const char *graph,
+					    const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create graph stats cluster to aggregate runtime node stats.
+ *
+ * @param prm
+ *   Parameters including file pointer to dump stats,
+ *   Graph pattern to create cluster and callback function.
+ *
+ * @return
+ *   Valid pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_graph_cluster_stats *rte_graph_cluster_stats_create(
+			const struct rte_graph_cluster_stats_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy cluster stats.
+ *
+ * @param stat
+ *    Valid cluster pointer to destroy.
+ *
+ */
+__rte_experimental
+void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get stats to application.
+ *
+ * @param[out] stat
+ *   Cluster status.
+ * @param skip_cb
+ *   true to skip callback function invocation.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat,
+				 bool skip_cb);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Reset cluster stats to zero.
+ *
+ * @param stat
+ *   Valid cluster stats pointer.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register new packet processing node. Nodes can be registered
+ * dynamically via this call or statically via the RTE_NODE_REGISTER
+ * macro.
+ *
+ * @param node
+ *   Valid node pointer with name, process function and next_nodes.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ *
+ * @see RTE_NODE_REGISTER()
+ */
+__rte_experimental
+rte_node_t __rte_node_register(const struct rte_node_register *node);
+
+/**
+ * Register a static node.
+ *
+ * The static node is registered through the constructor scheme, thereby, it can
+ * be used in a multi-process scenario.
+ *
+ * @param node
+ *   Valid node pointer with name, process function, and next_nodes.
+ */
+#define RTE_NODE_REGISTER(node)                                                \
+	RTE_INIT(rte_node_register_##node)                                     \
+	{                                                                      \
+		node.parent_id = RTE_NODE_ID_INVALID;                          \
+		node.id = __rte_node_register(&node);                          \
+	}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Clone a node from static node(node created from RTE_NODE_REGISTER).
+ *
+ * @param id
+ *   Static node id to clone from.
+ * @param name
+ *   Name of the new node. The library prepends the parent node name to the
+ * user-specified the name. The final node name will be,
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_clone(rte_node_t id, const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node id from node name.
+ *
+ * @param name
+ *   Valid node name. In the case of the cloned node, the name will be
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node name from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid node name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_node_id_to_name(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the number of edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_count(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Update the edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ * @param from
+ *   Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
+ * in that case, it will be added to the end of the list.
+ * @param next_nodes
+ *   Name of the edges to update.
+ * @param nb_edges
+ *   Number of edges to update.
+ *
+ * @return
+ *   Valid edge count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
+				const char **next_nodes, uint16_t nb_edges);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Shrink the edges to a given size.
+ *
+ * @param id
+ *   Valid node id.
+ * @param size
+ *   New size to shrink the edges.
+ *
+ * @return
+ *   New size on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the edge names from a given node.
+ *
+ * @param id
+ *   Valid node id.
+ * @param[out] next_nodes
+ *   Buffer to copy the edge names. The NULL value is allowed in that case,
+ * the function returns the size of the array that needs to be allocated.
+ *
+ * @return
+ *   When next_nodes == NULL, it returns the size of the array else
+ *  number of item copied.
+ */
+__rte_experimental
+rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum nodes created so far.
+ *
+ * @return
+ *   Maximum nodes count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ * @param id
+ *   Node id to get the info.
+ */
+__rte_experimental
+void rte_node_dump(FILE *f, rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ */
+__rte_experimental
+void rte_node_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of node id.
+ *
+ * @param id
+ *   Node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_node_is_invalid(rte_node_t id)
+{
+	return (id == RTE_NODE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of edge id.
+ *
+ * @param id
+ *   Edge node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_edge_is_invalid(rte_edge_t id)
+{
+	return (id == RTE_EDGE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of graph id.
+ *
+ * @param id
+ *   Graph id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_is_invalid(rte_graph_t id)
+{
+	return (id == RTE_GRAPH_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test stats feature support.
+ *
+ * @return
+ *   1 if stats enabled, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_has_stats_feature(void)
+{
+#ifdef RTE_LIBRTE_GRAPH_STATS
+	return RTE_LIBRTE_GRAPH_STATS;
+#else
+	return 0;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_H_ */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
new file mode 100644
index 000000000..55ef35df5
--- /dev/null
+++ b/lib/librte_graph/rte_graph_version.map
@@ -0,0 +1,3 @@
+EXPERIMENTAL {
+
+};
diff --git a/lib/meson.build b/lib/meson.build
index 9c3cc55d5..c43d86bb9 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index d295ca0a5..b1195f09a 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -98,6 +98,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
+_LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 02/29] graph: implement node registration
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-04-03 10:44       ` Wang, Xiao W
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 03/29] graph: implement node operations jerinj
                       ` (27 subsequent siblings)
  29 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding rte_node_register() API implementation includes allocating
memory for node object, check for duplicate node name and
add the allocated node to STAILQ node_list for future use.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph.c               |  18 +++-
 lib/librte_graph/graph_private.h       |  75 ++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/node.c                | 115 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   4 +
 6 files changed, 213 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/node.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 26fe514f3..933d0ee49 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -14,6 +14,7 @@ LDLIBS += -lrte_eal
 EXPORT_MAP := rte_graph_version.map
 
 # all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a55bf443a..a9c124896 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,4 +2,20 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
-#include "rte_graph.h"
+#include <rte_spinlock.h>
+
+#include "graph_private.h"
+
+static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+
+void
+graph_spinlock_lock(void)
+{
+	rte_spinlock_lock(&graph_lock);
+}
+
+void
+graph_spinlock_unlock(void)
+{
+	rte_spinlock_unlock(&graph_lock);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
new file mode 100644
index 000000000..8b9ff5292
--- /dev/null
+++ b/lib/librte_graph/graph_private.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_PRIVATE_H_
+#define _RTE_GRAPH_PRIVATE_H_
+
+#include <inttypes.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_eal.h>
+
+#include "rte_graph.h"
+
+/**
+ * @internal
+ *
+ * Structure that holds node internal data.
+ */
+struct node {
+	STAILQ_ENTRY(node) next;      /**< Next node in the list. */
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+	rte_node_process_t process;   /**< Node process function. */
+	rte_node_init_t init;         /**< Node init function. */
+	rte_node_fini_t fini;	      /**< Node fini function. */
+	rte_node_t id;		      /**< Allocated identifier for the node. */
+	rte_node_t parent_id;	      /**< Parent node identifier. */
+	rte_edge_t nb_edges;	      /**< Number of edges from this node. */
+	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
+};
+
+/* Node functions */
+STAILQ_HEAD(node_head, node);
+
+/**
+ * @internal
+ *
+ * Get the head of the node list.
+ *
+ * @return
+ *   Pointer to the node head.
+ */
+struct node_head *node_list_head_get(void);
+
+/**
+ * @internal
+ *
+ * Get node pointer from node name.
+ *
+ * @param name
+ *   Pointer to character string containing the node name.
+ *
+ * @return
+ *   Pointer to the node.
+ */
+struct node *node_from_name(const char *name);
+
+/* Lock functions */
+/**
+ * @internal
+ *
+ * Take a lock on the graph internal spin lock.
+ */
+void graph_spinlock_lock(void);
+
+/**
+ * @internal
+ *
+ * Release a lock on the graph internal spin lock.
+ */
+void graph_spinlock_unlock(void);
+
+#endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 455cf2ba5..5754ac23b 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('graph.c')
+sources = files('node.c', 'graph.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
new file mode 100644
index 000000000..7999ca6ed
--- /dev/null
+++ b/lib/librte_graph/node.c
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_string_fns.h>
+
+#include "graph_private.h"
+
+static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
+static rte_node_t node_id;
+
+#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)
+
+/* Private functions */
+struct node_head *
+node_list_head_get(void)
+{
+	return &node_list;
+}
+
+struct node *
+node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static bool
+node_has_duplicate_entry(const char *name)
+{
+	struct node *node;
+
+	/* Is duplicate name registered */
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0) {
+			rte_errno = EEXIST;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Public functions */
+rte_node_t
+__rte_node_register(const struct rte_node_register *reg)
+{
+	struct node *node;
+	rte_edge_t i;
+	size_t sz;
+
+	graph_spinlock_lock();
+
+	/* Check sanity */
+	if (reg == NULL || reg->process == NULL) {
+		rte_errno = EINVAL;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(reg->name))
+		goto fail;
+
+	sz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);
+	node = calloc(1, sz);
+	if (node == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Initialize the node */
+	if (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {
+		rte_errno = E2BIG;
+		goto free;
+	}
+	node->flags = reg->flags;
+	node->process = reg->process;
+	node->init = reg->init;
+	node->fini = reg->fini;
+	node->nb_edges = reg->nb_edges;
+	node->parent_id = reg->parent_id;
+	for (i = 0; i < reg->nb_edges; i++) {
+		if (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto free;
+		}
+	}
+
+	node->id = node_id++;
+
+	/* Add the node at tail */
+	STAILQ_INSERT_TAIL(&node_list, node, next);
+	graph_spinlock_unlock();
+
+	return node->id;
+free:
+	free(node);
+fail:
+	graph_spinlock_unlock();
+	return RTE_NODE_ID_INVALID;
+}
+
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 55ef35df5..0884c09f1 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -1,3 +1,7 @@
 EXPERIMENTAL {
+	global:
 
+	__rte_node_register;
+
+	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 03/29] graph: implement node operations
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 02/29] graph: implement node registration jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-04-03 10:54       ` Wang, Xiao W
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 04/29] graph: implement node debug routines jerinj
                       ` (26 subsequent siblings)
  29 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding node-specific API implementation like cloning node, updating
edges for the node, shrinking edges of a node, retrieving edges of a
node.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph_private.h       |  10 +
 lib/librte_graph/node.c                | 269 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  10 +
 3 files changed, 289 insertions(+)

diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 8b9ff5292..7ed6d01b6 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+
+#define ID_CHECK(id, id_max)                                                   \
+	do {                                                                   \
+		if ((id) >= (id_max)) {                                        \
+			rte_errno = EINVAL;                                    \
+			goto fail;                                             \
+		}                                                              \
+	} while (0)
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 7999ca6ed..8de857889 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -113,3 +113,272 @@ __rte_node_register(const struct rte_node_register *reg)
 	return RTE_NODE_ID_INVALID;
 }
 
+static int
+clone_name(struct rte_node_register *reg, struct node *node, const char *name)
+{
+	ssize_t sz, rc;
+
+#define SZ RTE_NODE_NAMESIZE
+	rc = rte_strscpy(reg->name, node->name, SZ);
+	if (rc < 0)
+		goto fail;
+	sz = rc;
+	rc = rte_strscpy(reg->name + sz, "-", RTE_MAX((int16_t)(SZ - sz), 0));
+	if (rc < 0)
+		goto fail;
+	sz += rc;
+	sz = rte_strscpy(reg->name + sz, name, RTE_MAX((int16_t)(SZ - sz), 0));
+	if (sz < 0)
+		goto fail;
+
+	return 0;
+fail:
+	rte_errno = E2BIG;
+	return -rte_errno;
+}
+
+static rte_node_t
+node_clone(struct node *node, const char *name)
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct rte_node_register *reg;
+	rte_edge_t i;
+
+	/* Don't allow to clone a node from a cloned node */
+	if (node->parent_id != RTE_NODE_ID_INVALID) {
+		rte_errno = EEXIST;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(name))
+		goto fail;
+
+	reg = calloc(1, sizeof(*reg) + (sizeof(char *) * node->nb_edges));
+	if (reg == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Clone the source node */
+	reg->flags = node->flags;
+	reg->process = node->process;
+	reg->init = node->init;
+	reg->fini = node->fini;
+	reg->nb_edges = node->nb_edges;
+	reg->parent_id = node->id;
+
+	for (i = 0; i < node->nb_edges; i++)
+		reg->next_nodes[i] = node->next_nodes[i];
+
+	/* Naming ceremony of the new node. name is node->name + "-" + name */
+	if (clone_name(reg, node, name))
+		goto free;
+
+	rc = __rte_node_register(reg);
+free:
+	free(reg);
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_clone(rte_node_t id, const char *name)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node_clone(node, name);
+
+fail:
+	return RTE_NODE_ID_INVALID;
+}
+
+rte_node_t
+rte_node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node->id;
+
+	return RTE_NODE_ID_INVALID;
+}
+
+char *
+rte_node_id_to_name(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->name;
+
+fail:
+	return NULL;
+}
+
+rte_edge_t
+rte_node_edge_count(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->nb_edges;
+fail:
+	return RTE_EDGE_ID_INVALID;
+}
+
+static rte_edge_t
+edge_update(struct node *node, struct node *prev, rte_edge_t from,
+	    const char **next_nodes, rte_edge_t nb_edges)
+{
+	rte_edge_t i, max_edges, count = 0;
+	struct node *new_node;
+	bool need_realloc;
+	size_t sz;
+
+	if (from == RTE_EDGE_ID_INVALID)
+		from = node->nb_edges;
+
+	/* Don't create hole in next_nodes[] list */
+	if (from > node->nb_edges) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Remove me from list */
+	STAILQ_REMOVE(&node_list, node, node, next);
+
+	/* Allocate the storage space for new node if required */
+	max_edges = from + nb_edges;
+	need_realloc = max_edges > node->nb_edges;
+	if (need_realloc) {
+		sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
+		new_node = realloc(node, sz);
+		if (new_node == NULL) {
+			rte_errno = ENOMEM;
+			goto restore;
+		} else {
+			node = new_node;
+		}
+	}
+
+	/* Update the new nodes name */
+	for (i = from; i < max_edges; i++, count++) {
+		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto restore;
+		}
+	}
+restore:
+	/* Update the linked list to point new node address in prev node */
+	if (prev)
+		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
+	else
+		STAILQ_INSERT_HEAD(&node_list, node, next);
+
+	if (need_realloc)
+		node->nb_edges += count;
+
+fail:
+	return count;
+}
+
+rte_edge_t
+rte_node_edge_shrink(rte_node_t id, rte_edge_t size)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (node->nb_edges < size) {
+				rte_errno = E2BIG;
+				goto fail;
+			}
+			node->nb_edges = size;
+			rc = size;
+			break;
+		}
+	}
+
+fail:
+	graph_spinlock_unlock();
+	return rc;
+}
+
+rte_edge_t
+rte_node_edge_update(rte_node_t id, rte_edge_t from, const char **next_nodes,
+		     uint16_t nb_edges)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *n, *prev;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	prev = NULL;
+	STAILQ_FOREACH(n, &node_list, next) {
+		if (n->id == id) {
+			rc = edge_update(n, prev, from, next_nodes, nb_edges);
+			break;
+		}
+		prev = n;
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+static rte_node_t
+node_copy_edges(struct node *node, char *next_nodes[])
+{
+	rte_edge_t i;
+
+	for (i = 0; i < node->nb_edges; i++)
+		next_nodes[i] = node->next_nodes[i];
+
+	return i;
+}
+
+rte_node_t
+rte_node_edge_get(rte_node_t id, char *next_nodes[])
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (next_nodes == NULL)
+				rc = sizeof(char *) * node->nb_edges;
+			else
+				rc = node_copy_edges(node, next_nodes);
+			break;
+		}
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_max_count(void)
+{
+	return node_id;
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 0884c09f1..412386356 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,5 +3,15 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 
+	rte_node_clone;
+	rte_node_edge_count;
+	rte_node_edge_get;
+	rte_node_edge_shrink;
+	rte_node_edge_update;
+	rte_node_from_name;
+	rte_node_id_to_name;
+	rte_node_list_dump;
+	rte_node_max_count;
+
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 04/29] graph: implement node debug routines
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (2 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 03/29] graph: implement node operations jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-04-04  7:57       ` Wang, Xiao W
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph operation helpers jerinj
                       ` (25 subsequent siblings)
  29 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding node debug API implementation support to dump
single or all the node objects to the given file.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |  1 +
 lib/librte_graph/graph_debug.c         | 25 ++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 12 ++++++++++
 lib/librte_graph/meson.build           |  2 +-
 lib/librte_graph/node.c                | 32 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 6 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_debug.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 933d0ee49..2a6d86933 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
new file mode 100644
index 000000000..75238e7ca
--- /dev/null
+++ b/lib/librte_graph/graph_debug.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_debug.h>
+
+#include "graph_private.h"
+
+void
+node_dump(FILE *f, struct node *n)
+{
+	rte_edge_t i;
+
+	fprintf(f, "node <%s>\n", n->name);
+	fprintf(f, "  id=%" PRIu32 "\n", n->id);
+	fprintf(f, "  flags=0x%" PRIx64 "\n", n->flags);
+	fprintf(f, "  addr=%p\n", n);
+	fprintf(f, "  process=%p\n", n->process);
+	fprintf(f, "  nb_edges=%d\n", n->nb_edges);
+
+	for (i = 0; i < n->nb_edges; i++)
+		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
+}
+
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7ed6d01b6..6db04cee7 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -82,4 +82,16 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/**
+ * @internal
+ *
+ * Dump internal node object data.
+ *
+ * @param f
+ *   FILE pointer to dump the info.
+ * @param g
+ *   Pointer to the internal node object.
+ */
+void node_dump(FILE *f, struct node *n);
+
 #endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 5754ac23b..01512182f 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c')
+sources = files('node.c', 'graph.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 8de857889..2f9c2ea4c 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
 	return rc;
 }
 
+static void
+node_scan_dump(FILE *f, rte_node_t id, bool all)
+{
+	struct node *node;
+
+	RTE_ASSERT(f != NULL);
+	NODE_ID_CHECK(id);
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (all == true) {
+			node_dump(f, node);
+		} else if (node->id == id) {
+			node_dump(f, node);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_node_dump(FILE *f, rte_node_t id)
+{
+	node_scan_dump(f, id, false);
+}
+
+void
+rte_node_list_dump(FILE *f)
+{
+	node_scan_dump(f, 0, true);
+}
+
 rte_node_t
 rte_node_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 412386356..f2c2139c5 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	__rte_node_register;
 
 	rte_node_clone;
+	rte_node_dump;
 	rte_node_edge_count;
 	rte_node_edge_get;
 	rte_node_edge_shrink;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph operation helpers
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (3 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 04/29] graph: implement node debug routines jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-04-06 13:47       ` Wang, Xiao W
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 06/29] graph: populate fastpath memory for graph reel jerinj
                       ` (24 subsequent siblings)
  29 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding internal graph API helpers support to check whether a graph has
isolated nodes and any node have a loop to itself and BFS
algorithm implementation etc.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile        |   1 +
 lib/librte_graph/graph.c         |   2 +-
 lib/librte_graph/graph_ops.c     | 169 ++++++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h | 173 +++++++++++++++++++++++++++++++
 lib/librte_graph/meson.build     |   2 +-
 5 files changed, 345 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_ops.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 2a6d86933..39ecb2652 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a9c124896..4c3f2fe7b 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -7,7 +7,7 @@
 #include "graph_private.h"
 
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
-
+int rte_graph_logtype;
 void
 graph_spinlock_lock(void)
 {
diff --git a/lib/librte_graph/graph_ops.c b/lib/librte_graph/graph_ops.c
new file mode 100644
index 000000000..335595311
--- /dev/null
+++ b/lib/librte_graph/graph_ops.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+
+#include "graph_private.h"
+
+/* Check whether a node has next_node to itself */
+static inline int
+node_has_loop_edge(struct node *node)
+{
+	rte_edge_t i;
+	char *name;
+	int rc = 0;
+
+	for (i = 0; i < node->nb_edges; i++) {
+		if (strncmp(node->name, node->next_nodes[i],
+			    RTE_NODE_NAMESIZE) == 0) {
+			name = node->name;
+			rc = 1;
+			SET_ERR_JMP(EINVAL, fail, "Node %s has loop to self",
+				    name);
+		}
+	}
+fail:
+	return rc;
+}
+
+int
+graph_node_has_loop_edge(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (node_has_loop_edge(graph_node->node))
+			return 1;
+
+	return 0;
+}
+
+rte_node_t
+graph_src_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t rc = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F)
+			rc++;
+
+	if (rc == 0)
+		SET_ERR_JMP(EINVAL, fail, "Graph needs at least a source node");
+fail:
+	return rc;
+}
+
+/* Check whether a node has next_node to a source node */
+int
+graph_node_has_edge_to_src_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *node;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			node = graph_node->adjacency_list[i]->node;
+			if (node->flags & RTE_NODE_SOURCE_F)
+				SET_ERR_JMP(
+					EEXIST, fail,
+					"Node %s points to the source node %s",
+					graph_node->node->name, node->name);
+		}
+	}
+
+	return 0;
+fail:
+	return 1;
+}
+
+rte_node_t
+graph_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t count = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		count++;
+
+	return count;
+}
+
+void
+graph_mark_nodes_as_not_visited(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		graph_node->visited = false;
+}
+
+int
+graph_bfs(struct graph *graph, struct graph_node *start)
+{
+	struct graph_node **queue, *v, *tmp;
+	uint16_t head = 0, tail = 0;
+	rte_edge_t i;
+	size_t sz;
+
+	sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
+	queue = malloc(sz);
+	if (queue == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
+			    sz);
+
+	/* BFS algorithm */
+	queue[tail++] = start;
+	start->visited = true;
+	while (head != tail) {
+		v = queue[head++];
+		for (i = 0; i < v->node->nb_edges; i++) {
+			tmp = v->adjacency_list[i];
+			if (tmp->visited == false) {
+				queue[tail++] = tmp;
+				tmp->visited = true;
+			}
+		}
+	}
+
+	free(queue);
+	return 0;
+
+fail:
+	return -rte_errno;
+}
+
+/* Check whether a node has connected path or parent node */
+int
+graph_has_isolated_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	graph_mark_nodes_as_not_visited(graph);
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			if (graph_node->node->nb_edges == 0)
+				SET_ERR_JMP(EINVAL, fail,
+					    "%s node needs minimum one edge",
+					    graph_node->node->name);
+			if (graph_bfs(graph, graph_node))
+				goto fail;
+		}
+	}
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->visited == false)
+			SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
+				    graph_node->node->name);
+
+	return 0;
+fail:
+	return 1;
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 6db04cee7..220a35e2a 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+extern int rte_graph_logtype;
+
+#define GRAPH_LOG(level, ...)                                                  \
+	rte_log(RTE_LOG_##level, rte_graph_logtype,                            \
+		RTE_FMT("GRAPH: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",    \
+			__func__, __LINE__, RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define graph_err(...) GRAPH_LOG(ERR, __VA_ARGS__)
+#define graph_info(...) GRAPH_LOG(INFO, __VA_ARGS__)
+#define graph_dbg(...) GRAPH_LOG(DEBUG, __VA_ARGS__)
 
 #define ID_CHECK(id, id_max)                                                   \
 	do {                                                                   \
@@ -22,6 +32,12 @@
 		}                                                              \
 	} while (0)
 
+#define SET_ERR_JMP(err, where, fmt, ...)                                      \
+	do {                                                                   \
+		graph_err(fmt, ##__VA_ARGS__);                                 \
+		rte_errno = err;                                               \
+		goto where;                                                    \
+	} while (0)
 
 /**
  * @internal
@@ -41,6 +57,52 @@ struct node {
 	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
 };
 
+/**
+ * @internal
+ *
+ * Structure that holds the graph node data.
+ */
+struct graph_node {
+	STAILQ_ENTRY(graph_node) next; /**< Next graph node in the list. */
+	struct node *node; /**< Pointer to internal node. */
+	bool visited;      /**< Flag used in BFS to mark node visited. */
+	struct graph_node *adjacency_list[]; /**< Adjacency list of the node. */
+};
+
+/**
+ * @internal
+ *
+ * Structure that holds graph internal data.
+ */
+struct graph {
+	STAILQ_ENTRY(graph) next;
+	/**< List of graphs. */
+	char name[RTE_GRAPH_NAMESIZE];
+	/**< Name of the graph. */
+	const struct rte_memzone *mz;
+	/**< Memzone to store graph data. */
+	rte_graph_off_t nodes_start;
+	/**< Node memory start offset in graph reel. */
+	rte_node_t src_node_count;
+	/**< Number of source nodes in a graph. */
+	struct rte_graph *graph;
+	/**< Pointer to graph data. */
+	rte_node_t node_count;
+	/**< Total number of nodes. */
+	uint32_t cir_start;
+	/**< Circular buffer start offset in graph reel. */
+	uint32_t cir_mask;
+	/**< Circular buffer mask for wrap around. */
+	rte_graph_t id;
+	/**< Graph identifier. */
+	size_t mem_sz;
+	/**< Memory size of the graph. */
+	int socket;
+	/**< Socket identifier where memory is allocated. */
+	STAILQ_HEAD(gnode_list, graph_node) node_list;
+	/**< Nodes in a graph. */
+};
+
 /* Node functions */
 STAILQ_HEAD(node_head, node);
 
@@ -67,6 +129,19 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);
 
+/* Graph list functions */
+STAILQ_HEAD(graph_head, graph);
+
+/**
+ * @internal
+ *
+ * Get the head of the graph list.
+ *
+ * @return
+ *   Pointer to the graph head.
+ */
+struct graph_head *graph_list_head_get(void);
+
 /* Lock functions */
 /**
  * @internal
@@ -82,6 +157,104 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/* Graph operations */
+/**
+ * @internal
+ *
+ * Run a BFS(Breadth First Search) on the graph marking all
+ * the graph nodes as visited.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ * @param start
+ *   Pointer to the starting graph node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough memory for BFS.
+ */
+int graph_bfs(struct graph *graph, struct graph_node *start);
+
+/**
+ * @internal
+ *
+ * Check if there is an isolated node in the given graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: No isolated node found.
+ *   - 1: Isolated node found.
+ */
+int graph_has_isolated_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Check whether a node in the graph has next_node to a source node.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to source node.
+ *   - 1: Node doesn't have an edge to source node.
+ */
+int graph_node_has_edge_to_src_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Checks whether node in the graph has a edge to itself i.e. forms a
+ * loop.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to itself.
+ *   - 1: Node doesn't have an edge to itself.
+ */
+int graph_node_has_loop_edge(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of source nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of source nodes.
+ */
+rte_node_t graph_src_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of total number of nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of nodes.
+ */
+rte_node_t graph_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Clear the visited flag of all the nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ */
+void graph_mark_nodes_as_not_visited(struct graph *graph);
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 01512182f..16e0625c1 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_debug.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 06/29] graph: populate fastpath memory for graph reel
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (4 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph operation helpers jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 07/29] graph: implement create and destroy APIs jerinj
                       ` (23 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding support to create and populate the memory for graph reel.
This includes reserving the memory in the memzone, populating the nodes,
Allocating memory for node-specific streams to hold objects.

Once it is populated the reel memory contains the following sections.

+---------------------+
|   Graph Header      |
+---------------------+
|   Fence             |
+---------------------+
|   Circular buffer   |
+---------------------+
|   Fence             |
+---------------------+
|   Node Object 0     |
+------------------- -+
|   Node Object 1     |
+------------------- -+
|   Node Object 2     |
+------------------- -+
|   Node Object n     |
+------------------- -+

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   2 +
 lib/librte_graph/graph.c               |  16 ++
 lib/librte_graph/graph_populate.c      | 234 +++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       |  64 +++++++
 lib/librte_graph/meson.build           |   4 +-
 lib/librte_graph/node.c                |   5 +
 lib/librte_graph/rte_graph_version.map |   1 +
 lib/librte_graph/rte_graph_worker.h    | 108 ++++++++++++
 8 files changed, 432 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/rte_graph_worker.h

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 39ecb2652..7bfd7d51f 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,8 +18,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph_worker.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 4c3f2fe7b..e1930b7d2 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,6 +2,7 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <rte_malloc.h>
 #include <rte_spinlock.h>
 
 #include "graph_private.h"
@@ -19,3 +20,18 @@ graph_spinlock_unlock(void)
 {
 	rte_spinlock_unlock(&graph_lock);
 }
+
+void __rte_noinline
+__rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
diff --git a/lib/librte_graph/graph_populate.c b/lib/librte_graph/graph_populate.c
new file mode 100644
index 000000000..093512efa
--- /dev/null
+++ b/lib/librte_graph/graph_populate.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "graph_private.h"
+
+static size_t
+graph_fp_mem_calc_size(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t val;
+	size_t sz;
+
+	/* Graph header */
+	sz = sizeof(struct rte_graph);
+	/* Source nodes list */
+	sz += sizeof(rte_graph_off_t) * graph->src_node_count;
+	/* Circular buffer for pending streams of size number of nodes */
+	val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
+	sz = RTE_ALIGN(sz, val);
+	graph->cir_start = sz;
+	graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
+	sz += val;
+	/* Fence */
+	sz += sizeof(RTE_GRAPH_FENCE);
+	sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+	graph->nodes_start = sz;
+	/* For 0..N node objects with fence */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+		sz += sizeof(struct rte_node);
+		/* Pointer to next nodes(edges) */
+		sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
+	}
+
+	graph->mem_sz = sz;
+	return sz;
+}
+
+static void
+graph_header_popluate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+
+	graph->tail = 0;
+	graph->head = (int32_t)-_graph->src_node_count;
+	graph->cir_mask = _graph->cir_mask;
+	graph->nb_nodes = _graph->node_count;
+	graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
+	graph->nodes_start = _graph->nodes_start;
+	graph->socket = _graph->socket;
+	graph->id = _graph->id;
+	memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);
+	graph->fence = RTE_GRAPH_FENCE;
+}
+
+static void
+graph_nodes_populate(struct graph *_graph)
+{
+	rte_graph_off_t off = _graph->nodes_start;
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	rte_edge_t count, nb_edges;
+	const char *parent;
+	rte_node_t pid;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		struct rte_node *node = RTE_PTR_ADD(graph, off);
+		memset(node, 0, sizeof(*node));
+		node->fence = RTE_GRAPH_FENCE;
+		node->off = off;
+		node->process = graph_node->node->process;
+		memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
+		pid = graph_node->node->parent_id;
+		if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
+			parent = rte_node_id_to_name(pid);
+			memcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);
+		}
+		node->id = graph_node->node->id;
+		node->parent_id = pid;
+		nb_edges = graph_node->node->nb_edges;
+		node->nb_edges = nb_edges;
+		off += sizeof(struct rte_node);
+		/* Copy the name in first pass to replace with rte_node* later*/
+		for (count = 0; count < nb_edges; count++)
+			node->nodes[count] = (struct rte_node *)&graph_node
+						     ->adjacency_list[count]
+						     ->node->name[0];
+
+		off += sizeof(struct rte_node *) * nb_edges;
+		off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
+		node->next = off;
+		__rte_node_stream_alloc(graph, node);
+	}
+}
+
+struct rte_node *
+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (unlikely(node->id == id))
+			return node;
+
+	return NULL;
+}
+
+struct rte_node *
+graph_node_name_to_ptr(const struct rte_graph *graph, const char *name)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (strncmp(name, node->name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static int
+graph_node_nexts_populate(struct graph *_graph)
+{
+	rte_node_t count, val;
+	rte_graph_off_t off;
+	struct rte_node *node;
+	const struct rte_graph *graph = _graph->graph;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		for (val = 0; val < node->nb_edges; val++) {
+			name = (const char *)node->nodes[val];
+			node->nodes[val] = graph_node_name_to_ptr(graph, name);
+			if (node->nodes[val] == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_src_nodes_populate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	struct rte_node *node;
+	int32_t head = -1;
+	const char *name;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			name = graph_node->node->name;
+			node = graph_node_name_to_ptr(graph, name);
+			if (node == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+
+			__rte_node_stream_alloc(graph, node);
+			graph->cir_start[head--] = node->off;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_fp_mem_populate(struct graph *graph)
+{
+	int rc;
+
+	graph_header_popluate(graph);
+	graph_nodes_populate(graph);
+	rc = graph_node_nexts_populate(graph);
+	rc |= graph_src_nodes_populate(graph);
+
+	return rc;
+}
+
+int
+graph_fp_mem_create(struct graph *graph)
+{
+	const struct rte_memzone *mz;
+	size_t sz;
+
+	sz = graph_fp_mem_calc_size(graph);
+	mz = rte_memzone_reserve(graph->name, sz, graph->socket, 0);
+	if (mz == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Memzone %s reserve failed",
+			    graph->name);
+
+	graph->graph = mz->addr;
+	graph->mz = mz;
+
+	return graph_fp_mem_populate(graph);
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_nodes_mem_destroy(struct rte_graph *graph)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	if (graph == NULL)
+		return;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		rte_free(node->objs);
+}
+
+int
+graph_fp_mem_destroy(struct graph *graph)
+{
+	graph_nodes_mem_destroy(graph->graph);
+	return rte_memzone_free(graph->mz);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 220a35e2a..7fce52e00 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -12,6 +12,7 @@
 #include <rte_eal.h>
 
 #include "rte_graph.h"
+#include "rte_graph_worker.h"
 
 extern int rte_graph_logtype;
 
@@ -254,6 +255,69 @@ rte_node_t graph_nodes_count(struct graph *graph);
  */
 void graph_mark_nodes_as_not_visited(struct graph *graph);
 
+/* Fast path graph memory populate unctions */
+
+/**
+ * @internal
+ *
+ * Create fast-path memory for the graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough for graph and nodes.
+ *   - -EINVAL: Graph nodes not found.
+ */
+int graph_fp_mem_create(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Free fast-path memory used by graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Graph memzone related error.
+ */
+int graph_fp_mem_destroy(struct graph *graph);
+
+/* Lookup functions */
+/**
+ * @internal
+ *
+ * Get graph node object from node id.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param id
+ *   Node Identifier.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
+				      rte_node_t id);
+
+/**
+ * @internal
+ *
+ * Get graph node object from node name.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param node_name
+ *   Pointer to character string holding the node name.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
+					const char *node_name);
 
 /**
  * @internal
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 16e0625c1..fb203a5e2 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,8 +4,8 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
-headers = files('rte_graph.h')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
 deps += ['eal']
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 2f9c2ea4c..639269870 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -61,6 +61,11 @@ __rte_node_register(const struct rte_node_register *reg)
 	rte_edge_t i;
 	size_t sz;
 
+	/* Limit Node specific metadata to one cacheline on 64B CL machine */
+	RTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -
+			  offsetof(struct rte_node, ctx)) !=
+			 RTE_CACHE_LINE_MIN_SIZE);
+
 	graph_spinlock_lock();
 
 	/* Check sanity */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index f2c2139c5..a9fe1b610 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -2,6 +2,7 @@ EXPERIMENTAL {
 	global:
 
 	__rte_node_register;
+	__rte_node_stream_alloc;
 
 	rte_node_clone;
 	rte_node_dump;
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
new file mode 100644
index 000000000..a8133739d
--- /dev/null
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_WORKER_H_
+#define _RTE_GRAPH_WORKER_H_
+
+/**
+ * @file rte_graph_worker.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows a worker thread to walk over a graph and nodes to create,
+ * process, enqueue and move streams of objects to the next nodes.
+ */
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+
+#include "rte_graph.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ *
+ * Data structure to hold graph data.
+ */
+struct rte_graph {
+	uint32_t tail;		     /**< Tail of circular buffer. */
+	uint32_t head;		     /**< Head of circular buffer. */
+	uint32_t cir_mask;	     /**< Circular buffer wrap around mask. */
+	rte_node_t nb_nodes;	     /**< Number of nodes in the graph. */
+	rte_graph_off_t *cir_start;  /**< Pointer to circular buffer. */
+	rte_graph_off_t nodes_start; /**< Offset at which node memory starts. */
+	rte_graph_t id;	/**< Graph identifier. */
+	int socket;	/**< Socket ID where memory is allocated. */
+	char name[RTE_GRAPH_NAMESIZE];	/**< Name of the graph. */
+	uint64_t fence;			/**< Fence. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Data structure to hold node data.
+ */
+struct rte_node {
+	/* Slow path area  */
+	uint64_t fence;		/**< Fence. */
+	rte_graph_off_t next;	/**< Index to next node. */
+	rte_node_t id;		/**< Node identifier. */
+	rte_node_t parent_id;	/**< Parent Node identifier. */
+	rte_edge_t nb_edges;	/**< Number of edges from this node. */
+	uint32_t realloc_count;	/**< Number of times realloced. */
+
+	char parent[RTE_NODE_NAMESIZE];	/**< Parent node name. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+
+	/* Fast path area  */
+#define RTE_NODE_CTX_SZ 16
+	uint8_t ctx[RTE_NODE_CTX_SZ] __rte_cache_aligned; /**< Node Context. */
+	uint16_t size;		/**< Total number of objects available. */
+	uint16_t idx;		/**< Number of objects used. */
+	rte_graph_off_t off;	/**< Offset of node in the graph reel. */
+	uint64_t total_cycles;	/**< Cycles spent in this node. */
+	uint64_t total_calls;	/**< Calls done to this node. */
+	uint64_t total_objs;	/**< Objects processed by this node. */
+	RTE_STD_C11
+		union {
+			void **objs;	   /**< Array of object pointers. */
+			uint64_t objs_u64;
+		};
+	RTE_STD_C11
+		union {
+			rte_node_process_t process; /**< Process function. */
+			uint64_t process_u64;
+		};
+	struct rte_node *nodes[] __rte_cache_min_aligned; /**< Next nodes. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ */
+__rte_experimental
+void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_WORKER_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 07/29] graph: implement create and destroy APIs
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (5 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 06/29] graph: populate fastpath memory for graph reel jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 08/29] graph: implement graph operation APIs jerinj
                       ` (22 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding graph specific API implementations like graph create
and graph destroy. This detect loops in the graph,
check for isolated nodes and operation to verify the validity of
graph.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 320 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   2 +
 2 files changed, 322 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e1930b7d2..dc373231e 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,13 +2,33 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_errno.h>
 #include <rte_malloc.h>
+#include <rte_memzone.h>
 #include <rte_spinlock.h>
+#include <rte_string_fns.h>
 
 #include "graph_private.h"
 
+static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+static rte_graph_t graph_id;
 int rte_graph_logtype;
+
+#define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id)
+
+/* Private functions */
+struct graph_head *
+graph_list_head_get(void)
+{
+	return &graph_list;
+}
+
 void
 graph_spinlock_lock(void)
 {
@@ -21,6 +41,306 @@ graph_spinlock_unlock(void)
 	rte_spinlock_unlock(&graph_lock);
 }
 
+static int
+graph_node_add(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+	size_t sz;
+
+	/* Skip the duplicate nodes */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (strncmp(node->name, graph_node->node->name,
+			    RTE_NODE_NAMESIZE) == 0)
+			return 0;
+
+	/* Allocate new graph node object */
+	sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);
+	graph_node = calloc(1, sz);
+
+	if (graph_node == NULL)
+		SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object",
+			    node->name);
+
+	/* Initialize the graph node */
+	graph_node->node = node;
+
+	/* Add to graph node list */
+	STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);
+	return 0;
+
+free:
+	free(graph_node);
+	return -rte_errno;
+}
+
+static struct graph_node *
+node_to_graph_node(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node == node)
+			return graph_node;
+
+	SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name);
+fail:
+	return NULL;
+}
+
+static int
+graph_node_edges_add(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			if (graph_node_add(graph, adjacency))
+				goto fail;
+		}
+	}
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_adjacency_list_update(struct graph *graph)
+{
+	struct graph_node *graph_node, *tmp;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			tmp = node_to_graph_node(graph, adjacency);
+			if (tmp == NULL)
+				goto fail;
+			graph_node->adjacency_list[i] = tmp;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+expand_pattern_to_node(struct graph *graph, const char *pattern)
+{
+	struct node_head *node_head = node_list_head_get();
+	bool found = false;
+	struct node *node;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(node, node_head, next) {
+		if (fnmatch(pattern, node->name, 0) == 0) {
+			if (graph_node_add(graph, node))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_cleanup(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	while (!STAILQ_EMPTY(&graph->node_list)) {
+		graph_node = STAILQ_FIRST(&graph->node_list);
+		STAILQ_REMOVE_HEAD(&graph->node_list, next);
+		free(graph_node);
+	}
+}
+
+static int
+graph_node_init(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	const char *name;
+	int rc;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->init) {
+			name = graph_node->node->name;
+			rc = graph_node->node->init(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph, name));
+			if (rc)
+				SET_ERR_JMP(rc, err, "Node %s init() failed",
+					    name);
+		}
+	}
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+graph_node_fini(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->fini)
+			graph_node->node->fini(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph,
+						       graph_node->node->name));
+}
+
+rte_graph_t
+rte_graph_create(const char *name, struct rte_graph_param *prm)
+{
+	struct graph *graph;
+	const char *pattern;
+	uint16_t i;
+
+	graph_spinlock_lock();
+
+	/* Check arguments sanity */
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
+
+	if (name == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
+
+	/* Check for existence of duplicate graph */
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
+			SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
+				    name);
+
+	/* Create graph object */
+	graph = calloc(1, sizeof(*graph));
+	if (graph == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
+
+	/* Initialize the graph object */
+	STAILQ_INIT(&graph->node_list);
+	if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
+		SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
+
+	/* Expand node pattern and add the nodes to the graph */
+	for (i = 0; i < prm->nb_node_patterns; i++) {
+		pattern = prm->node_patterns[i];
+		if (expand_pattern_to_node(graph, pattern))
+			goto graph_cleanup;
+	}
+
+	/* Go over all the nodes edges and add them to the graph */
+	if (graph_node_edges_add(graph))
+		goto graph_cleanup;
+
+	/* Update adjacency list of all nodes in the graph */
+	if (graph_adjacency_list_update(graph))
+		goto graph_cleanup;
+
+	/* Make sure at least a source node present in the graph */
+	if (!graph_src_nodes_count(graph))
+		goto graph_cleanup;
+
+	/* Make sure no node is pointing to source node */
+	if (graph_node_has_edge_to_src_node(graph))
+		goto graph_cleanup;
+
+	/* Don't allow node has loop to self */
+	if (graph_node_has_loop_edge(graph))
+		goto graph_cleanup;
+
+	/* Do BFS from src nodes on the graph to find isolated nodes */
+	if (graph_has_isolated_node(graph))
+		goto graph_cleanup;
+
+	/* Initialize graph object */
+	graph->socket = prm->socket_id;
+	graph->src_node_count = graph_src_nodes_count(graph);
+	graph->node_count = graph_nodes_count(graph);
+	graph->id = graph_id;
+
+	/* Allocate the Graph fast path memory and populate the data */
+	if (graph_fp_mem_create(graph))
+		goto graph_cleanup;
+
+	/* Call init() of the all the nodes in the graph */
+	if (graph_node_init(graph))
+		goto graph_mem_destroy;
+
+	/* All good, Lets add the graph to the list */
+	graph_id++;
+	STAILQ_INSERT_TAIL(&graph_list, graph, next);
+
+	graph_spinlock_unlock();
+	return graph->id;
+
+graph_mem_destroy:
+	graph_fp_mem_destroy(graph);
+graph_cleanup:
+	graph_cleanup(graph);
+free:
+	free(graph);
+fail:
+	graph_spinlock_unlock();
+	return RTE_GRAPH_ID_INVALID;
+}
+
+rte_graph_t
+rte_graph_destroy(const char *graph_name)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph, *tmp;
+	const char *name;
+
+	graph_spinlock_lock();
+
+	graph = STAILQ_FIRST(&graph_list);
+	while (graph != NULL) {
+		tmp = STAILQ_NEXT(graph, next);
+		name = graph->name;
+		if (strncmp(name, graph_name, RTE_GRAPH_NAMESIZE) == 0) {
+			/* Call fini() of the all the nodes in the graph */
+			graph_node_fini(graph);
+			/* Destroy graph fast path memory */
+			rc = graph_fp_mem_destroy(graph);
+			if (rc)
+				SET_ERR_JMP(rc, done, "MZ %s free failed",
+					    name);
+
+			graph_cleanup(graph);
+			STAILQ_REMOVE(&graph_list, graph, graph, next);
+			rc = graph->id;
+			free(graph);
+			graph_id--;
+			goto done;
+		}
+		graph = tmp;
+	}
+done:
+	graph_spinlock_unlock();
+	return rc;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index a9fe1b610..dcbd78c02 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,8 @@ EXPERIMENTAL {
 	__rte_node_register;
 	__rte_node_stream_alloc;
 
+	rte_graph_create;
+	rte_graph_destroy;
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 08/29] graph: implement graph operation APIs
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (6 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 07/29] graph: implement create and destroy APIs jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 09/29] graph: implement Graphviz export jerinj
                       ` (21 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K, Anatoly Burakov
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding support for graph specific API implementation like
Graph lookup to get graph object, retrieving graph ID
From name and graph name from ID.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 131 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   8 ++
 2 files changed, 139 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index dc373231e..7c6a7897d 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -210,6 +210,54 @@ graph_node_fini(struct graph *graph)
 						       graph_node->node->name));
 }
 
+static struct rte_graph *
+graph_mem_fixup_node_ctx(struct rte_graph *graph)
+{
+	struct rte_node *node;
+	struct node *node_db;
+	rte_graph_off_t off;
+	rte_node_t count;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
+			name = node->name;
+		else /* Cloned node */
+			name = node->parent;
+
+		node_db = node_from_name(name);
+		if (node_db == NULL)
+			SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
+		node->process = node_db->process;
+	}
+
+	return graph;
+fail:
+	return NULL;
+}
+
+static struct rte_graph *
+graph_mem_fixup_secondray(struct rte_graph *graph)
+{
+	if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
+		return graph;
+
+	return graph_mem_fixup_node_ctx(graph);
+}
+
+struct rte_graph *
+rte_graph_lookup(const char *name)
+{
+	const struct rte_memzone *mz;
+	struct rte_graph *rc = NULL;
+
+	mz = rte_memzone_lookup(name);
+	if (mz)
+		rc = mz->addr;
+
+	return graph_mem_fixup_secondray(rc);
+}
+
 rte_graph_t
 rte_graph_create(const char *name, struct rte_graph_param *prm)
 {
@@ -341,6 +389,76 @@ rte_graph_destroy(const char *graph_name)
 	return rc;
 }
 
+rte_graph_t
+rte_graph_from_name(const char *name)
+{
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)
+			return graph->id;
+
+	return RTE_GRAPH_ID_INVALID;
+}
+
+char *
+rte_graph_id_to_name(rte_graph_t id)
+{
+	struct graph *graph;
+
+	GRAPH_ID_CHECK(id);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == id)
+			return graph->name;
+
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get(rte_graph_t gid, uint32_t nid)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	GRAPH_ID_CHECK(gid);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == gid) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (node->id == nid)
+					return node;
+			}
+			break;
+		}
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get_by_name(const char *graph_name, const char *node_name)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (!strncmp(node->name, node_name,
+					     RTE_NODE_NAMESIZE))
+					return node;
+			}
+			break;
+		}
+
+	return NULL;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
@@ -355,3 +473,16 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->size = size;
 	node->realloc_count++;
 }
+
+rte_graph_t
+rte_graph_max_count(void)
+{
+	return graph_id;
+}
+
+RTE_INIT(rte_graph_init_log)
+{
+	rte_graph_logtype = rte_log_register("lib.graph");
+	if (rte_graph_logtype >= 0)
+		rte_log_set_level(rte_graph_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index dcbd78c02..5a2b13293 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,14 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_from_name;
+	rte_graph_id_to_name;
+	rte_graph_lookup;
+	rte_graph_list_dump;
+	rte_graph_max_count;
+	rte_graph_node_get;
+	rte_graph_node_get_by_name;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 09/29] graph: implement Graphviz export
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (7 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 08/29] graph: implement graph operation APIs jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 10/29] graph: implement debug routines jerinj
                       ` (20 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding API implementation support exporting the graph object to file.
This will export the graph to a file in Graphviz format.
It can be viewed in many viewers such as
https://dreampuf.github.io/GraphvizOnline/

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 54 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 2 files changed, 55 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 7c6a7897d..e0c0b71a7 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -474,6 +474,60 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+static int
+graph_to_dot(FILE *f, struct graph *graph)
+{
+	const char *src_edge_color = " [color=blue]\n";
+	const char *edge_color = "\n";
+	struct graph_node *graph_node;
+	char *node_name;
+	rte_edge_t i;
+	int rc;
+
+	rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name);
+	if (rc < 0)
+		goto end;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		node_name = graph_node->node->name;
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name,
+				     graph_node->adjacency_list[i]->node->name,
+				     graph_node->node->flags & RTE_NODE_SOURCE_F
+					     ? src_edge_color
+					     : edge_color);
+			if (rc < 0)
+				goto end;
+		}
+	}
+	rc = fprintf(f, "}\n");
+	if (rc < 0)
+		goto end;
+
+	return 0;
+end:
+	rte_errno = EBADF;
+	return -rte_errno;
+}
+
+rte_graph_t
+rte_graph_export(const char *name, FILE *f)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {
+			rc = graph_to_dot(f, graph);
+			goto end;
+		}
+	}
+	rte_errno = ENOENT;
+end:
+	return rc;
+}
+
+
 rte_graph_t
 rte_graph_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 5a2b13293..2797be044 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
 	rte_graph_lookup;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 10/29] graph: implement debug routines
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (8 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 09/29] graph: implement Graphviz export jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 11/29] graph: implement stats support jerinj
                       ` (19 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph specific API to dump the
Graph information to a file. This API will dump detailed internal
info about node objects and graph objects.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 31 ++++++++++++++
 lib/librte_graph/graph_debug.c         | 59 ++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 13 ++++++
 lib/librte_graph/rte_graph_version.map |  2 +
 4 files changed, 105 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e0c0b71a7..e96363777 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -527,6 +527,37 @@ rte_graph_export(const char *name, FILE *f)
 	return rc;
 }
 
+static void
+graph_scan_dump(FILE *f, rte_graph_t id, bool all)
+{
+	struct graph *graph;
+
+	RTE_VERIFY(f);
+	GRAPH_ID_CHECK(id);
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (all == true) {
+			graph_dump(f, graph);
+		} else if (graph->id == id) {
+			graph_dump(f, graph);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_graph_dump(FILE *f, rte_graph_t id)
+{
+	graph_scan_dump(f, id, false);
+}
+
+void
+rte_graph_list_dump(FILE *f)
+{
+	graph_scan_dump(f, 0, true);
+}
 
 rte_graph_t
 rte_graph_max_count(void)
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
index 75238e7ca..f8aea16ac 100644
--- a/lib/librte_graph/graph_debug.c
+++ b/lib/librte_graph/graph_debug.c
@@ -7,6 +7,26 @@
 
 #include "graph_private.h"
 
+void
+graph_dump(FILE *f, struct graph *g)
+{
+	struct graph_node *graph_node;
+	rte_edge_t i = 0;
+
+	fprintf(f, "graph <%s>\n", g->name);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  cir_start=%" PRIu32 "\n", g->cir_start);
+	fprintf(f, "  cir_mask=%" PRIu32 "\n", g->cir_mask);
+	fprintf(f, "  addr=%p\n", g);
+	fprintf(f, "  graph=%p\n", g->graph);
+	fprintf(f, "  mem_sz=%zu\n", g->mem_sz);
+	fprintf(f, "  node_count=%" PRIu32 "\n", g->node_count);
+	fprintf(f, "  src_node_count=%" PRIu32 "\n", g->src_node_count);
+
+	STAILQ_FOREACH(graph_node, &g->node_list, next)
+		fprintf(f, "     node[%d] <%s>\n", i++, graph_node->node->name);
+}
+
 void
 node_dump(FILE *f, struct node *n)
 {
@@ -23,3 +43,42 @@ node_dump(FILE *f, struct node *n)
 		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
 }
 
+void
+rte_graph_obj_dump(FILE *f, struct rte_graph *g, bool all)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *n;
+	rte_edge_t i;
+
+	fprintf(f, "graph <%s> @ %p\n", g->name, g);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  head=%" PRId32 "\n", (int32_t)g->head);
+	fprintf(f, "  tail=%" PRId32 "\n", (int32_t)g->tail);
+	fprintf(f, "  cir_mask=0x%" PRIx32 "\n", g->cir_mask);
+	fprintf(f, "  nb_nodes=%" PRId32 "\n", g->nb_nodes);
+	fprintf(f, "  socket=%d\n", g->socket);
+	fprintf(f, "  fence=0x%" PRIx64 "\n", g->fence);
+	fprintf(f, "  nodes_start=0x%" PRIx32 "\n", g->nodes_start);
+	fprintf(f, "  cir_start=%p\n", g->cir_start);
+
+	rte_graph_foreach_node(count, off, g, n) {
+		if (!all && n->idx == 0)
+			continue;
+		fprintf(f, "     node[%d] <%s>\n", count, n->name);
+		fprintf(f, "       fence=0x%" PRIx64 "\n", n->fence);
+		fprintf(f, "       objs=%p\n", n->objs);
+		fprintf(f, "       process=%p\n", n->process);
+		fprintf(f, "       id=0x%" PRIx32 "\n", n->id);
+		fprintf(f, "       offset=0x%" PRIx32 "\n", n->off);
+		fprintf(f, "       nb_edges=%" PRId32 "\n", n->nb_edges);
+		fprintf(f, "       realloc_count=%d\n", n->realloc_count);
+		fprintf(f, "       size=%d\n", n->size);
+		fprintf(f, "       idx=%d\n", n->idx);
+		fprintf(f, "       total_objs=%" PRId64 "\n", n->total_objs);
+		fprintf(f, "       total_calls=%" PRId64 "\n", n->total_calls);
+		for (i = 0; i < n->nb_edges; i++)
+			fprintf(f, "          edge[%d] <%s>\n", i,
+				n->nodes[i]->name);
+	}
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7fce52e00..f9a85c892 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -319,6 +319,19 @@ struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
 struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
 					const char *node_name);
 
+/* Debug functions */
+/**
+ * @internal
+ *
+ * Dump internal graph object data.
+ *
+ * @param f
+ *   FILE pointer to dump the data.
+ * @param g
+ *   Pointer to the internal graph object.
+ */
+void graph_dump(FILE *f, struct graph *g);
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 2797be044..851f4772e 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_dump;
 	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
@@ -14,6 +15,7 @@ EXPERIMENTAL {
 	rte_graph_max_count;
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
+	rte_graph_obj_dump;
 
 	rte_node_clone;
 	rte_node_dump;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 11/29] graph: implement stats support
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (9 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 10/29] graph: implement debug routines jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 12/29] graph: implement fastpath API routines jerinj
                       ` (18 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph stats collection API. This API will
create a cluster for a specified node pattern and aggregate the node
runtime stats.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph_stats.c         | 406 +++++++++++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/rte_graph_version.map |   5 +
 4 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_stats.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 7bfd7d51f..967c8d9bc 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,6 +18,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_stats.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
diff --git a/lib/librte_graph/graph_stats.c b/lib/librte_graph/graph_stats.c
new file mode 100644
index 000000000..125e08d73
--- /dev/null
+++ b/lib/librte_graph/graph_stats.c
@@ -0,0 +1,406 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+
+#include "graph_private.h"
+
+/* Capture all graphs of cluster */
+struct cluster {
+	rte_graph_t nb_graphs;
+	rte_graph_t size;
+
+	struct graph **graphs;
+};
+
+/* Capture same node ID across cluster  */
+struct cluster_node {
+	struct rte_graph_cluster_node_stats stat;
+	rte_node_t nb_nodes;
+
+	struct rte_node *nodes[];
+};
+
+struct rte_graph_cluster_stats {
+	/* Header */
+	rte_graph_cluster_stats_cb_t fn;
+	uint32_t cluster_node_size; /* Size of struct cluster_node */
+	rte_node_t max_nodes;
+	int socket_id;
+	void *cookie;
+	size_t sz;
+
+	struct cluster_node clusters[];
+} __rte_cache_aligned;
+
+#define boarder()                                                              \
+	fprintf(f, "+-------------------------------+---------------+--------" \
+		   "-------+---------------+---------------+---------------+-" \
+		   "----------+\n")
+
+static inline void
+print_banner(FILE *f)
+{
+	boarder();
+	fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
+		"|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
+		"|cycles/call|");
+	boarder();
+}
+
+static inline void
+print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
+{
+	double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
+	const uint64_t prev_calls = stat->prev_calls;
+	const uint64_t prev_objs = stat->prev_objs;
+	const uint64_t cycles = stat->cycles;
+	const uint64_t calls = stat->calls;
+	const uint64_t objs = stat->objs;
+	uint64_t call_delta;
+
+	call_delta = calls - prev_calls;
+	objs_per_call =
+		call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
+	cycles_per_call =
+		call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
+			   : 0;
+	ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
+	objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
+	objs_per_sec /= 1000000;
+
+	fprintf(f,
+		"|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
+		"|%-15.3f|%-15.6f|%-11.4f|\n",
+		stat->name, calls, objs, stat->realloc_count, objs_per_call,
+		objs_per_sec, cycles_per_call);
+}
+
+static int
+graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
+		       const struct rte_graph_cluster_node_stats *stat)
+{
+	FILE *f = cookie;
+
+	if (unlikely(is_first))
+		print_banner(f);
+	if (stat->objs)
+		print_node(f, stat);
+	if (unlikely(is_last))
+		boarder();
+
+	return 0;
+};
+
+static struct rte_graph_cluster_stats *
+stats_mem_init(struct cluster *cluster,
+	       const struct rte_graph_cluster_stats_param *prm)
+{
+	size_t sz = sizeof(struct rte_graph_cluster_stats);
+	struct rte_graph_cluster_stats *stats;
+	rte_graph_cluster_stats_cb_t fn;
+	int socket_id = prm->socket_id;
+	uint32_t cluster_node_size;
+
+	/* Fix up callback */
+	fn = prm->fn;
+	if (fn == NULL)
+		fn = graph_cluster_stats_cb;
+
+	cluster_node_size = sizeof(struct cluster_node);
+	/* For a given cluster, max nodes will be the max number of graphs */
+	cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
+	cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
+
+	stats = realloc(NULL, sz);
+	memset(stats, 0, sz);
+	if (stats) {
+		stats->fn = fn;
+		stats->cluster_node_size = cluster_node_size;
+		stats->max_nodes = 0;
+		stats->socket_id = socket_id;
+		stats->cookie = prm->cookie;
+		stats->sz = sz;
+	}
+
+	return stats;
+}
+
+static int
+stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
+		   struct rte_graph *graph, struct graph_node *graph_node)
+{
+	struct rte_graph_cluster_stats *stats = *stats_in;
+	rte_node_t id = graph_node->node->id;
+	struct cluster_node *cluster;
+	struct rte_node *node;
+	rte_node_t count;
+
+	cluster = stats->clusters;
+
+	/* Iterate over cluster node array to find node ID match */
+	for (count = 0; count < stats->max_nodes; count++) {
+		/* Found an existing node in the reel */
+		if (cluster->stat.id == id) {
+			node = graph_node_id_to_ptr(graph, id);
+			if (node == NULL)
+				SET_ERR_JMP(
+					ENOENT, err,
+					"Failed to find node %s in graph %s",
+					graph_node->node->name, graph->name);
+
+			cluster->nodes[cluster->nb_nodes++] = node;
+			return 0;
+		}
+		cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
+	}
+
+	/* Hey, it is a new node, allocate space for it in the reel */
+	stats = realloc(stats, stats->sz + stats->cluster_node_size);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, err, "Realloc failed");
+
+	/* Clear the new struct cluster_node area */
+	cluster = RTE_PTR_ADD(stats, stats->sz),
+	memset(cluster, 0, stats->cluster_node_size);
+	memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
+	cluster->stat.id = graph_node->node->id;
+	cluster->stat.hz = rte_get_timer_hz();
+	node = graph_node_id_to_ptr(graph, id);
+	if (node == NULL)
+		SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s",
+			    graph_node->node->name, graph->name);
+	cluster->nodes[cluster->nb_nodes++] = node;
+
+	stats->sz += stats->cluster_node_size;
+	stats->max_nodes++;
+	*stats_in = stats;
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+stats_mem_fini(struct rte_graph_cluster_stats *stats)
+{
+	free(stats);
+}
+
+static void
+cluster_init(struct cluster *cluster)
+{
+	memset(cluster, 0, sizeof(*cluster));
+}
+
+static int
+cluster_add(struct cluster *cluster, struct graph *graph)
+{
+	rte_graph_t count;
+	size_t sz;
+
+	/* Skip the if graph is already added to cluster */
+	for (count = 0; count < cluster->nb_graphs; count++)
+		if (cluster->graphs[count] == graph)
+			return 0;
+
+	/* Expand the cluster if required to store graph objects */
+	if (cluster->nb_graphs + 1 > cluster->size) {
+		cluster->size = RTE_MAX(1, cluster->size * 2);
+		sz = sizeof(struct graph *) * cluster->size;
+		cluster->graphs = realloc(cluster->graphs, sz);
+		if (cluster->graphs == NULL)
+			SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
+	}
+
+	/* Add graph to cluster */
+	cluster->graphs[cluster->nb_graphs++] = graph;
+	return 0;
+
+free:
+	return -rte_errno;
+}
+
+static void
+cluster_fini(struct cluster *cluster)
+{
+	if (cluster->graphs)
+		free(cluster->graphs);
+}
+
+static int
+expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
+{
+	struct graph_head *graph_head = graph_list_head_get();
+	struct graph *graph;
+	bool found = false;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(graph, graph_head, next) {
+		if (fnmatch(pattern, graph->name, 0) == 0) {
+			if (cluster_add(cluster, graph))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
+			    pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+struct rte_graph_cluster_stats *
+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
+{
+	struct rte_graph_cluster_stats *stats, *rc = NULL;
+	struct graph_node *graph_node;
+	struct cluster cluster;
+	struct graph *graph;
+	const char *pattern;
+	rte_graph_t i;
+
+	/* Sanity checks */
+	if (!rte_graph_has_stats_feature())
+		SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
+
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Invalid param");
+
+	if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
+		SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
+
+	cluster_init(&cluster);
+
+	graph_spinlock_lock();
+	/* Expand graph pattern and add the graph to the cluster */
+	for (i = 0; i < prm->nb_graph_patterns; i++) {
+		pattern = prm->graph_patterns[i];
+		if (expand_pattern_to_cluster(&cluster, pattern))
+			goto bad_pattern;
+	}
+
+	/* Alloc the stats memory */
+	stats = stats_mem_init(&cluster, prm);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
+
+	/* Iterate over M(Graph) x N (Nodes in graph) */
+	for (i = 0; i < cluster.nb_graphs; i++) {
+		graph = cluster.graphs[i];
+		STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+			struct rte_graph *graph_fp = graph->graph;
+			if (stats_mem_populate(&stats, graph_fp, graph_node))
+				goto realloc_fail;
+		}
+	}
+
+	/* Finally copy to hugepage memory to avoid pressure on rte_realloc */
+	rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
+	if (rc)
+		rte_memcpy(rc, stats, stats->sz);
+	else
+		SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
+
+realloc_fail:
+	stats_mem_fini(stats);
+bad_pattern:
+	graph_spinlock_unlock();
+	cluster_fini(&cluster);
+fail:
+	return rc;
+}
+
+void
+rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
+{
+	return rte_free(stat);
+}
+
+static inline void
+cluster_node_arregate_stats(struct cluster_node *cluster)
+{
+	uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+	struct rte_node *node;
+	rte_node_t count;
+
+	for (count = 0; count < cluster->nb_nodes; count++) {
+		node = cluster->nodes[count];
+
+		calls += node->total_calls;
+		objs += node->total_objs;
+		cycles += node->total_cycles;
+		realloc_count += node->realloc_count;
+	}
+
+	stat->calls = calls;
+	stat->objs = objs;
+	stat->cycles = cycles;
+	stat->ts = rte_get_timer_cycles();
+	stat->realloc_count = realloc_count;
+}
+
+static inline void
+cluster_node_store_prev_stats(struct cluster_node *cluster)
+{
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+
+	stat->prev_ts = stat->ts;
+	stat->prev_calls = stat->calls;
+	stat->prev_objs = stat->objs;
+	stat->prev_cycles = stat->cycles;
+}
+
+void
+rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+	int rc = 0;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		cluster_node_arregate_stats(cluster);
+		if (!skip_cb)
+			rc = stat->fn(!count, (count == stat->max_nodes - 1),
+				      stat->cookie, &cluster->stat);
+		cluster_node_store_prev_stats(cluster);
+		if (rc)
+			break;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
+
+void
+rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		struct rte_graph_cluster_node_stats *node = &cluster->stat;
+
+		node->ts = 0;
+		node->calls = 0;
+		node->objs = 0;
+		node->cycles = 0;
+		node->prev_ts = 0;
+		node->prev_calls = 0;
+		node->prev_objs = 0;
+		node->prev_cycles = 0;
+		node->realloc_count = 0;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index fb203a5e2..929a17f84 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_stats.c', 'graph_populate.c')
 headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 851f4772e..adf55d406 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -17,6 +17,11 @@ EXPERIMENTAL {
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
 
+	rte_graph_cluster_stats_create;
+	rte_graph_cluster_stats_destroy;
+	rte_graph_cluster_stats_get;
+	rte_graph_cluster_stats_reset;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 12/29] graph: implement fastpath API routines
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (10 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 11/29] graph: implement stats support jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 13/29] graph: add unit test case jerinj
                       ` (17 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for rte_graph_walk() API. This will perform a walk
on the circular buffer and call the process function of each node
and collect the stats if stats collection is enabled.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md              |   1 +
 lib/librte_graph/graph.c               |  16 +
 lib/librte_graph/rte_graph_version.map |  10 +
 lib/librte_graph/rte_graph_worker.h    | 434 +++++++++++++++++++++++++
 4 files changed, 461 insertions(+)

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5cc50f750..fd2ff64d7 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -160,6 +160,7 @@ The public API headers are grouped by topics:
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
+    [graph_worker]     (@ref rte_graph_worker.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e96363777..d5d816c71 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -474,6 +474,22 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+void __rte_noinline
+__rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,
+			     uint16_t req_size)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
+
 static int
 graph_to_dot(FILE *f, struct graph *graph)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index adf55d406..13b838752 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 	__rte_node_stream_alloc;
+	__rte_node_stream_alloc_size;
 
 	rte_graph_create;
 	rte_graph_destroy;
@@ -16,6 +17,7 @@ EXPERIMENTAL {
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
+	rte_graph_walk;
 
 	rte_graph_cluster_stats_create;
 	rte_graph_cluster_stats_destroy;
@@ -28,10 +30,18 @@ EXPERIMENTAL {
 	rte_node_edge_get;
 	rte_node_edge_shrink;
 	rte_node_edge_update;
+	rte_node_enqueue;
+	rte_node_enqueue_x1;
+	rte_node_enqueue_x2;
+	rte_node_enqueue_x4;
+	rte_node_enqueue_next;
 	rte_node_from_name;
 	rte_node_id_to_name;
 	rte_node_list_dump;
 	rte_node_max_count;
+	rte_node_next_stream_get;
+	rte_node_next_stream_put;
+	rte_node_next_stream_move;
 
 	local: *;
 };
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
index a8133739d..a1bfc498b 100644
--- a/lib/librte_graph/rte_graph_worker.h
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -101,6 +101,440 @@ struct rte_node {
 __rte_experimental
 void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
 
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream with requested number of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param req_size
+ *   Number of objects to be allocated.
+ */
+__rte_experimental
+void __rte_node_stream_alloc_size(struct rte_graph *graph,
+				  struct rte_node *node, uint16_t req_size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Perform graph walk on the circular buffer and invoke the process function
+ * of the nodes and collect the stats.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup function.
+ *
+ * @see rte_graph_lookup()
+ */
+__rte_experimental
+static inline void
+rte_graph_walk(struct rte_graph *graph)
+{
+	const rte_graph_off_t *cir_start = graph->cir_start;
+	const rte_node_t mask = graph->cir_mask;
+	uint32_t head = graph->head;
+	struct rte_node *node;
+	uint64_t start;
+	uint16_t rc;
+	void **objs;
+
+	/*
+	 * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
+	 * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
+	 * in a circular buffer fashion.
+	 *
+	 *	+-----+ <= cir_start - head [number of source nodes]
+	 *	|     |
+	 *	| ... | <= source nodes
+	 *	|     |
+	 *	+-----+ <= cir_start [head = 0] [tail = 0]
+	 *	|     |
+	 *	| ... | <= pending streams
+	 *	|     |
+	 *	+-----+ <= cir_start + mask
+	 */
+	while (likely(head != graph->tail)) {
+		node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
+		RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+		objs = node->objs;
+		rte_prefetch0(objs);
+
+		if (rte_graph_has_stats_feature()) {
+			start = rte_rdtsc();
+			rc = node->process(graph, node, objs, node->idx);
+			node->total_cycles += rte_rdtsc() - start;
+			node->total_calls++;
+			node->total_objs += rc;
+		} else {
+			node->process(graph, node, objs, node->idx);
+		}
+		node->idx = 0;
+		head = likely((int32_t)head > 0) ? head & mask : head;
+	}
+	graph->tail = 0;
+}
+
+/* Fast path helper functions */
+
+/**
+ * @internal
+ *
+ * Enqueue a given node to the tail of the graph reel.
+ *
+ * @param graph
+ *   Pointer Graph object.
+ * @param node
+ *   Pointer to node object to be enqueued.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_tail_update(struct rte_graph *graph, struct rte_node *node)
+{
+	uint32_t tail;
+
+	tail = graph->tail;
+	graph->cir_start[tail++] = node->off;
+	graph->tail = tail & graph->cir_mask;
+}
+
+/**
+ * @internal
+ *
+ * Enqueue sequence prologue function.
+ *
+ * Updates the node to tail of graph reel and resizes the number of objects
+ * available in the stream as needed.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param idx
+ *   Index at which the object enqueue starts from.
+ * @param space
+ *   Space required for the object enqueue.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_prologue(struct rte_graph *graph, struct rte_node *node,
+			    const uint16_t idx, const uint16_t space)
+{
+
+	/* Add to the pending stream list if the node is new */
+	if (idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	if (unlikely(node->size < (idx + space)))
+		__rte_node_stream_alloc(graph, node);
+}
+
+/**
+ * @internal
+ *
+ * Get the node pointer from current node edge id.
+ *
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Edge id of the required node.
+ *
+ * @return
+ *   Pointer to the node denoted by the edge id.
+ */
+static __rte_always_inline struct rte_node *
+__rte_node_next_node_get(struct rte_node *node, rte_edge_t next)
+{
+	RTE_ASSERT(next < node->nb_edges);
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+	node = node->nodes[next];
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+
+	return node;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue the objs to next node for further processing and set
+ * the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param objs
+ *   Objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
+		 rte_edge_t next, void **objs, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, nb_objs);
+
+	rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
+	node->idx = idx + nb_objs;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only one obj to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x1(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 1);
+
+	node->objs[idx++] = obj;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only two objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue two objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   Obj to enqueue.
+ * @param obj1
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x2(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 2);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only four objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue four objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   1st obj to enqueue.
+ * @param obj1
+ *   2nd obj to enqueue.
+ * @param obj2
+ *   3rd obj to enqueue.
+ * @param obj3
+ *   4th obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x4(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1, void *obj2,
+		    void *obj3)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 4);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->objs[idx++] = obj2;
+	node->objs[idx++] = obj3;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue objs to multiple next nodes for further processing and
+ * set the next nodes to pending state in the circular buffer.
+ * objs[i] will be enqueued to nexts[i].
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param nexts
+ *   List of relative next node indices to enqueue objs.
+ * @param objs
+ *   List of objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
+		      rte_edge_t *nexts, void **objs, uint16_t nb_objs)
+{
+	uint16_t i;
+
+	for (i = 0; i < nb_objs; i++)
+		rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the stream of next node to enqueue the objs.
+ * Once done with the updating the objs, needs to call
+ * rte_node_next_stream_put to put the next node to pending state.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to get stream.
+ * @param nb_objs
+ *   Requested free size of the next stream.
+ *
+ * @return
+ *   Valid next stream on success.
+ *
+ * @see rte_node_next_stream_put().
+ */
+__rte_experimental
+static inline void **
+rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+	uint16_t free_space = node->size - idx;
+
+	if (unlikely(free_space < nb_objs))
+		__rte_node_stream_alloc_size(graph, node, nb_objs);
+
+	return &node->objs[idx];
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Put the next stream to pending state in the circular buffer
+ * for further processing. Should be invoked followed by
+ * rte_node_next_stream_get().
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index..
+ * @param idx
+ *   Number of objs updated in the stream after getting the stream using
+ *   rte_node_next_stream_get.
+ *
+ * @see rte_node_next_stream_get().
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t idx)
+{
+	if (unlikely(!idx))
+		return;
+
+	node = __rte_node_next_node_get(node, next);
+	if (node->idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	node->idx += idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Home run scenario, Enqueue all the objs of current node to next
+ * node in optimized way by swapping the streams of both nodes.
+ * Performs good when next node is already not in pending state.
+ * If next node is already in pending state then normal enqueue
+ * will be used.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param src
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index.
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_move(struct rte_graph *graph, struct rte_node *src,
+			  rte_edge_t next)
+{
+	struct rte_node *dst = __rte_node_next_node_get(src, next);
+
+	/* Let swap the pointers if dst don't have valid objs */
+	if (likely(dst->idx == 0)) {
+		void **dobjs = dst->objs;
+		uint16_t dsz = dst->size;
+		dst->objs = src->objs;
+		dst->size = src->size;
+		src->objs = dobjs;
+		src->size = dsz;
+		dst->idx = src->idx;
+		__rte_node_enqueue_tail_update(graph, dst);
+	} else { /* Move the objects from src node to dst node */
+		rte_node_enqueue(graph, src, next, src->objs, src->idx);
+	}
+}
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 13/29] graph: add unit test case
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (11 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 12/29] graph: implement fastpath API routines jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 14/29] graph: add performance testcase jerinj
                       ` (16 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram

From: Kiran Kumar K <kirankumark@marvell.com>

Adding the unit test to test the functionality of node and graph APIs.
Testing includes registering a node, cloning a node, creating a graph,
perform graph walk, collecting stats and all node and graph debug APIs.

example command to test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 app/test/Makefile     |   6 +
 app/test/meson.build  |   6 +-
 app/test/test_graph.c | 819 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 830 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_graph.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 1f080d162..ce2e08e12 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -221,6 +221,10 @@ SRCS-y += test_event_timer_adapter.c
 SRCS-y += test_event_crypto_adapter.c
 endif
 
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
+SRCS-y += test_graph.c
+endif
+
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
 SRCS-y += test_rawdev.c
 endif
@@ -240,6 +244,8 @@ endif
 CFLAGS += -DALLOW_EXPERIMENTAL_API
 
 CFLAGS += -O3
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
 CFLAGS += $(WERROR_FLAGS)
 
 LDLIBS += -lm
diff --git a/app/test/meson.build b/app/test/meson.build
index 351d29cb6..3cf850584 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -51,6 +51,7 @@ test_sources = files('commands.c',
 	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
+	'test_graph.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
@@ -151,7 +152,8 @@ test_deps = ['acl',
 	'rib',
 	'ring',
 	'stack',
-	'timer'
+	'timer',
+	'graph'
 ]
 
 # Each test is marked with flag true/false
@@ -362,6 +364,8 @@ endif
 
 # specify -D_GNU_SOURCE unconditionally
 cflags += '-D_GNU_SOURCE'
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
 
 test_dep_objs = []
 if dpdk_conf.has('RTE_LIBRTE_COMPRESSDEV')
diff --git a/app/test/test_graph.c b/app/test/test_graph.c
new file mode 100644
index 000000000..a90dc8f30
--- /dev/null
+++ b/app/test/test_graph.c
@@ -0,0 +1,819 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static uint16_t test_node_worker_source(struct rte_graph *graph,
+					struct rte_node *node, void **objs,
+					uint16_t nb_objs);
+
+static uint16_t test_node0_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node1_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node2_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node3_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+#define MBUFF_SIZE 512
+#define MAX_NODES  4
+
+static struct rte_mbuf mbuf[MAX_NODES + 1][MBUFF_SIZE];
+static void *mbuf_p[MAX_NODES + 1][MBUFF_SIZE];
+static rte_graph_t graph_id;
+static uint64_t obj_stats[MAX_NODES + 1];
+static uint64_t fn_calls[MAX_NODES + 1];
+
+const char *node_patterns[] = {
+	"test_node_source1",	   "test_node00",
+	"test_node00-test_node11", "test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+const char *node_names[] = {
+	"test_node00",
+	"test_node00-test_node11",
+	"test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+struct test_node_register {
+	char name[RTE_NODE_NAMESIZE];
+	rte_node_process_t process;
+	uint16_t nb_edges;
+	const char *next_nodes[MAX_NODES];
+};
+
+typedef struct {
+	uint32_t idx;
+	struct test_node_register node;
+} test_node_t;
+
+typedef struct {
+	test_node_t test_node[MAX_NODES];
+} test_main_t;
+
+static test_main_t test_main = {
+	.test_node = {
+		{
+			.node = {
+					.name = "test_node00",
+					.process = test_node0_worker,
+					.nb_edges = 2,
+					.next_nodes = {"test_node00-"
+						       "test_node11",
+						       "test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node11",
+					.process = test_node1_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node22",
+					.process = test_node2_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node33"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node33",
+					.process = test_node3_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00"},
+				},
+		},
+	},
+};
+
+static int
+node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	*(uint32_t *)node->ctx = node->id;
+
+	return 0;
+}
+
+static struct rte_node_register test_node_source = {
+	.name = "test_node_source1",
+	.process = test_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.nb_edges = 2,
+	.init = node_init,
+	.next_nodes = {"test_node00", "test_node00-test_node11"},
+};
+RTE_NODE_REGISTER(test_node_source);
+
+static struct rte_node_register test_node0 = {
+	.name = "test_node00",
+	.process = test_node0_worker,
+	.init = node_init,
+};
+RTE_NODE_REGISTER(test_node0);
+
+uint16_t
+test_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	uint32_t obj_node0 = rand() % 100, obj_node1;
+	test_main_t *tm = &test_main;
+	struct rte_mbuf *data;
+	void **next_stream;
+	rte_node_t next;
+	uint32_t i;
+
+	RTE_SET_USED(objs);
+	nb_objs = RTE_GRAPH_BURST_SIZE;
+
+	/* Prepare stream for next node 0 */
+	obj_node0 = nb_objs * obj_node0 * 0.01;
+	next = 0;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node0);
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[0][i];
+		data->udata64 = ((uint64_t)tm->test_node[0].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][i];
+	}
+	rte_node_next_stream_put(graph, node, next, obj_node0);
+
+	/* Prepare stream for next node 1 */
+	obj_node1 = nb_objs - obj_node0;
+	next = 1;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node1);
+	for (i = 0; i < obj_node1; i++) {
+		data = &mbuf[0][obj_node0 + i];
+		data->udata64 = ((uint64_t)tm->test_node[1].idx << 32) | i;
+		if ((i + 1) == obj_node1)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][obj_node0 + i];
+	}
+
+	rte_node_next_stream_put(graph, node, next, obj_node1);
+	obj_stats[0] += nb_objs;
+	fn_calls[0] += 1;
+	return nb_objs;
+}
+
+uint16_t
+test_node0_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+
+	if (*(uint32_t *)node->ctx == test_node0.id) {
+		uint32_t obj_node0 = rand() % 100, obj_node1;
+		struct rte_mbuf *data;
+		uint8_t second_pass = 0;
+		uint32_t count = 0;
+		uint32_t i;
+
+		obj_stats[1] += nb_objs;
+		fn_calls[1] += 1;
+
+		for (i = 0; i < nb_objs; i++) {
+			data = (struct rte_mbuf *)objs[i];
+			if ((data->udata64 >> 32) != tm->test_node[0].idx) {
+				printf("Data idx miss match at node 0, expected"
+				       " = %u got = %u\n",
+				       tm->test_node[0].idx,
+				       (uint32_t)(data->udata64 >> 32));
+				goto end;
+			}
+
+			if ((data->udata64 & 0xffff) != (i - count)) {
+				printf("Expected buff count miss match at "
+				       "node 0\n");
+				goto end;
+			}
+
+			if (data->udata64 & (0x1 << 16))
+				count = i + 1;
+			if (data->udata64 & (0x1 << 17))
+				second_pass = 1;
+		}
+
+		if (count != i) {
+			printf("Count mismatch at node 0\n");
+			goto end;
+		}
+
+		obj_node0 = nb_objs * obj_node0 * 0.01;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[1][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[1].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[1][0],
+				 obj_node0);
+
+		obj_node1 = nb_objs - obj_node0;
+		for (i = 0; i < obj_node1; i++) {
+			data = &mbuf[1][obj_node0 + i];
+			data->udata64 =
+				((uint64_t)tm->test_node[2].idx << 32) | i;
+			if ((i + 1) == obj_node1)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 1, (void **)&mbuf_p[1][obj_node0],
+				 obj_node1);
+
+	} else if (*(uint32_t *)node->ctx == tm->test_node[1].idx) {
+		test_node1_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[2].idx) {
+		test_node2_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[3].idx) {
+		test_node3_worker(graph, node, objs, nb_objs);
+	} else {
+		printf("Unexpected node context\n");
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node1_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	uint32_t obj_node0 = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t i;
+
+	obj_stats[2] += nb_objs;
+	fn_calls[2] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[1].idx) {
+			printf("Data idx miss match at node 1, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[1].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 1\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 1\n");
+		goto end;
+	}
+
+	obj_node0 = nb_objs;
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[2][i];
+		data->udata64 = ((uint64_t)tm->test_node[2].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		if (second_pass)
+			data->udata64 |= (1 << 17);
+	}
+	rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[2][0], obj_node0);
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node2_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[3] += nb_objs;
+	fn_calls[3] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[2].idx) {
+			printf("Data idx miss match at node 2, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[2].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 2\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 2\n");
+		goto end;
+	}
+
+	if (!second_pass) {
+		obj_node0 = nb_objs;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[3][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[3].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[3][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node3_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[4] += nb_objs;
+	fn_calls[4] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[3].idx) {
+			printf("Data idx miss match at node 3, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[3].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 3\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 3\n");
+		goto end;
+	}
+
+	if (second_pass) {
+		printf("Unexpected buffers are at node 3\n");
+		goto end;
+	} else {
+		obj_node0 = nb_objs * 2;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[4][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[0].idx << 32) | i;
+			data->udata64 |= (1 << 17);
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[4][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+static int
+test_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	int i;
+
+	/* Verify the name with ID */
+	for (i = 1; i < MAX_NODES; i++) {
+		char *name = rte_node_id_to_name(tm->test_node[i].idx);
+		if (strcmp(name, node_names[i]) != 0) {
+			printf("Test node name verify by ID = %d failed "
+			       "Expected = %s, got %s\n",
+			       i, node_names[i], name);
+			return -1;
+		}
+	}
+
+	/* Verify by name */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t idx = rte_node_from_name(node_names[i]);
+		if (idx != tm->test_node[i].idx) {
+			printf("Test node ID verify by name = %s failed "
+			       "Expected = %d, got %d\n",
+			       node_names[i], tm->test_node[i].idx, idx);
+			return -1;
+		}
+	}
+
+	/* Verify edge count */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t count = rte_node_edge_count(tm->test_node[i].idx);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+	}
+
+	/* Verify edge names */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t j, count;
+		char **next_edges;
+
+		count = rte_node_edge_get(tm->test_node[i].idx, NULL);
+		if (count != tm->test_node[i].node.nb_edges * sizeof(char *)) {
+			printf("Test number of edge count for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+		next_edges = malloc(count);
+		count = rte_node_edge_get(tm->test_node[i].idx, next_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		for (j = 0; j < count; j++) {
+			if (strcmp(next_edges[j],
+				   tm->test_node[i].node.next_nodes[j]) != 0) {
+				printf("Edge name miss match, expected = %s got = %s\n",
+				       tm->test_node[i].node.next_nodes[j],
+				       next_edges[j]);
+				return -1;
+			}
+		}
+		free(next_edges);
+	}
+
+	return 0;
+}
+
+static int
+test_node_clone(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id, dummy_id;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	tm->test_node[0].idx = node_id;
+
+	/* Clone with same name, should fail */
+	dummy_id = rte_node_clone(node_id, "test_node00");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid id when clone with same name, Expecting fail\n");
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		tm->test_node[i].idx =
+			rte_node_clone(node_id, tm->test_node[i].node.name);
+		if (rte_node_is_invalid(tm->test_node[i].idx)) {
+			printf("Got invalid node id\n");
+			return -1;
+		}
+	}
+
+	/* Clone from cloned node should fail */
+	dummy_id = rte_node_clone(tm->test_node[1].idx, "dummy_node");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid node id when cloning from cloned node, expected fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_update_edges(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id;
+	uint16_t count;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	count = rte_node_edge_update(node_id, 0,
+				     tm->test_node[0].node.next_nodes,
+				     tm->test_node[0].node.nb_edges);
+	if (count != tm->test_node[0].node.nb_edges) {
+		printf("Update edges failed expected: %d got = %d\n",
+		       tm->test_node[0].node.nb_edges, count);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		count = rte_node_edge_update(tm->test_node[i].idx, 0,
+					     tm->test_node[i].node.next_nodes,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Update edges failed expected: %d got = %d\n",
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		count = rte_node_edge_shrink(tm->test_node[i].idx,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Shrink edges failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_create_graph(void)
+{
+	static const char *node_patterns_dummy[] = {
+		"test_node_source1",	   "test_node00",
+		"test_node00-test_node11", "test_node00-test_node22",
+		"test_node00-test_node33", "test_node00-dummy_node",
+	};
+	struct rte_graph_param gconf = {
+		.socket_id = SOCKET_ID_ANY,
+		.nb_node_patterns = 6,
+		.node_patterns = node_patterns_dummy,
+	};
+	uint32_t dummy_node_id;
+	uint32_t node_id;
+
+	node_id = rte_node_from_name("test_node00");
+	dummy_node_id = rte_node_clone(node_id, "dummy_node");
+	if (rte_node_is_invalid(dummy_node_id)) {
+		printf("Got invalid node id\n");
+		return -1;
+	}
+
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id != RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation success with isolated node, expected graph creation fail\n");
+		return -1;
+	}
+
+	gconf.nb_node_patterns = 5;
+	gconf.node_patterns = node_patterns;
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+test_graph_walk(void)
+{
+	struct rte_graph *graph = rte_graph_lookup("worker0");
+	int i;
+
+	if (!graph) {
+		printf("Graph lookup failed\n");
+		return -1;
+	}
+
+	for (i = 0; i < 5; i++)
+		rte_graph_walk(graph);
+	return 0;
+}
+
+static int
+test_graph_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	struct rte_node *node;
+	int i;
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get(graph_id, tm->test_node[i].idx);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get_by_name("worker0", node_names[i]);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+graph_cluster_stats_cb_t(bool is_first, bool is_last, void *cookie,
+			 const struct rte_graph_cluster_node_stats *st)
+{
+	int i;
+
+	RTE_SET_USED(is_first);
+	RTE_SET_USED(is_last);
+	RTE_SET_USED(cookie);
+
+	for (i = 0; i < MAX_NODES + 1; i++) {
+		rte_node_t id = rte_node_from_name(node_patterns[i]);
+		if (id == st->id) {
+			if (obj_stats[i] != st->objs) {
+				printf("Obj count miss match for node = %s expected = %"PRId64", got=%"PRId64"\n",
+				       node_patterns[i], obj_stats[i],
+				       st->objs);
+				return -1;
+			}
+
+			if (fn_calls[i] != st->calls) {
+				printf("Func call miss match for node = %s expected = %"PRId64", got = %"PRId64"\n",
+				       node_patterns[i], fn_calls[i],
+				       st->calls);
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+test_print_stats(void)
+{
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker0";
+
+	if (!rte_graph_has_stats_feature())
+		return 0;
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+	s_param.fn = graph_cluster_stats_cb_t;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL) {
+		printf("Unable to get stats\n");
+		return -1;
+	}
+	/* Clear screen and move to top left */
+	rte_graph_cluster_stats_get(stats, 0);
+	rte_graph_cluster_stats_destroy(stats);
+
+	return 0;
+}
+
+static int
+graph_setup(void)
+{
+	int i, j;
+
+	for (i = 0; i <= MAX_NODES; i++) {
+		for (j = 0; j < MBUFF_SIZE; j++)
+			mbuf_p[i][j] = &mbuf[i][j];
+	}
+	if (test_node_clone()) {
+		printf("test_node_clone: fail\n");
+		return -1;
+	}
+	printf("test_node_clone: pass\n");
+
+	return 0;
+}
+
+static void
+graph_teardown(void)
+{
+	rte_graph_t id;
+
+	id = rte_graph_destroy("worker0");
+	if (id == RTE_GRAPH_ID_INVALID)
+		printf("Graph Destroy failed\n");
+}
+
+static struct unit_test_suite graph_testsuite = {
+	.suite_name = "Graph library test suite",
+	.setup = graph_setup,
+	.teardown = graph_teardown,
+	.unit_test_cases = {
+		TEST_CASE(test_update_edges),
+		TEST_CASE(test_lookup_functions),
+		TEST_CASE(test_create_graph),
+		TEST_CASE(test_graph_lookup_functions),
+		TEST_CASE(test_graph_walk),
+		TEST_CASE(test_print_stats),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+graph_autotest_fn(void)
+{
+	return unit_test_suite_runner(&graph_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_autotest, graph_autotest_fn);
+
+static int
+test_node_list_dump(void)
+{
+	rte_node_list_dump(stdout);
+
+	return TEST_SUCCESS;
+}
+REGISTER_TEST_COMMAND(node_list_dump, test_node_list_dump);
+
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 14/29] graph: add performance testcase
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (12 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 13/29] graph: add unit test case jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 15/29] node: add log infra and null node jerinj
                       ` (15 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add unit test framework to create and test performance of various graph
models.

example command to test:

echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 app/test/Makefile          |    1 +
 app/test/meson.build       |    1 +
 app/test/test_graph_perf.c | 1057 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1059 insertions(+)
 create mode 100644 app/test/test_graph_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index ce2e08e12..77276f300 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -223,6 +223,7 @@ endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
 SRCS-y += test_graph.c
+SRCS-y += test_graph_perf.c
 endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
diff --git a/app/test/meson.build b/app/test/meson.build
index 3cf850584..9006cc074 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -52,6 +52,7 @@ test_sources = files('commands.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_graph.c',
+	'test_graph_perf.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
diff --git a/app/test/test_graph_perf.c b/app/test/test_graph_perf.c
new file mode 100644
index 000000000..a629f1e35
--- /dev/null
+++ b/app/test/test_graph_perf.c
@@ -0,0 +1,1057 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_lcore.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+#define TEST_GRAPH_PERF_MZ	     "graph_perf_data"
+#define TEST_GRAPH_SRC_NAME	     "test_graph_perf_source"
+#define TEST_GRAPH_SRC_BRST_ONE_NAME "test_graph_perf_source_one"
+#define TEST_GRAPH_WRK_NAME	     "test_graph_perf_worker"
+#define TEST_GRAPH_SNK_NAME	     "test_graph_perf_sink"
+
+#define SOURCES(map)	     RTE_DIM(map)
+#define STAGES(map)	     RTE_DIM(map)
+#define NODES_PER_STAGE(map) RTE_DIM(map[0])
+#define SINKS(map)	     RTE_DIM(map[0])
+
+#define MAX_EDGES_PER_NODE 7
+
+struct test_node_data {
+	uint8_t node_id;
+	uint8_t is_sink;
+	uint8_t next_nodes[MAX_EDGES_PER_NODE];
+	uint8_t next_percentage[MAX_EDGES_PER_NODE];
+};
+
+struct test_graph_perf {
+	uint16_t nb_nodes;
+	rte_graph_t graph_id;
+	struct test_node_data *node_data;
+};
+
+struct graph_lcore_data {
+	uint8_t done;
+	rte_graph_t graph_id;
+};
+
+static struct test_node_data *
+graph_get_node_data(struct test_graph_perf *graph_data, rte_node_t id)
+{
+	struct test_node_data *node_data = NULL;
+	int i;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		if (graph_data->node_data[i].node_id == id) {
+			node_data = &graph_data->node_data[i];
+			break;
+		}
+
+	return node_data;
+}
+
+static int
+test_node_ctx_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	struct test_graph_perf *graph_data;
+	struct test_node_data *node_data;
+	const struct rte_memzone *mz;
+	rte_node_t nid = node->id;
+	rte_edge_t edge = 0;
+	int i;
+
+	RTE_SET_USED(graph);
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+	node_data = graph_get_node_data(graph_data, nid);
+	node->ctx[0] = node->nb_edges;
+	for (i = 0; i < node->nb_edges && !node_data->is_sink; i++, edge++) {
+		node->ctx[i + 1] = edge;
+		node->ctx[i + 9] = node_data->next_percentage[i];
+	}
+
+	return 0;
+}
+
+/* Source node function */
+static uint16_t
+test_perf_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			     void **objs, uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9] * RTE_GRAPH_BURST_SIZE) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return RTE_GRAPH_BURST_SIZE;
+}
+
+static struct rte_node_register test_graph_perf_source = {
+	.name = TEST_GRAPH_SRC_NAME,
+	.process = test_perf_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source);
+
+static uint16_t
+test_perf_node_worker_source_burst_one(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9]) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return 1;
+}
+
+static struct rte_node_register test_graph_perf_source_burst_one = {
+	.name = TEST_GRAPH_SRC_BRST_ONE_NAME,
+	.process = test_perf_node_worker_source_burst_one,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source_burst_one);
+
+/* Worker node function */
+static uint16_t
+test_perf_node_worker(struct rte_graph *graph, struct rte_node *node,
+		      void **objs, uint16_t nb_objs)
+{
+	uint16_t next = 0;
+	uint16_t enq = 0;
+	uint16_t count;
+	int i;
+
+	/* Move stream for single next node */
+	if (node->ctx[0] == 1) {
+		rte_node_next_stream_move(graph, node, node->ctx[1]);
+		return nb_objs;
+	}
+
+	/* Enqueue objects to next nodes proportionally */
+	for (i = 0; i < node->ctx[0]; i++) {
+		next = node->ctx[i + 1];
+		count = (node->ctx[i + 9] * nb_objs) / 100;
+		enq += count;
+		while (count) {
+			switch (count & (4 - 1)) {
+			case 0:
+				rte_node_enqueue_x4(graph, node, next, objs[0],
+						    objs[1], objs[2], objs[3]);
+				objs += 4;
+				count -= 4;
+				break;
+			case 1:
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 1;
+				count -= 1;
+				break;
+			case 2:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				objs += 2;
+				count -= 2;
+				break;
+			case 3:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 3;
+				count -= 3;
+				break;
+			}
+		}
+	}
+
+	if (enq != nb_objs)
+		rte_node_enqueue(graph, node, next, objs, nb_objs - enq);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_worker = {
+	.name = TEST_GRAPH_WRK_NAME,
+	.process = test_perf_node_worker,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_worker);
+
+/* Last node in graph a.k.a sink node */
+static uint16_t
+test_perf_node_sink(struct rte_graph *graph, struct rte_node *node, void **objs,
+		    uint16_t nb_objs)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_sink = {
+	.name = TEST_GRAPH_SNK_NAME,
+	.process = test_perf_node_sink,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_sink);
+
+static int
+graph_perf_setup(void)
+{
+	if (rte_lcore_count() < 2) {
+		printf("Test requires at least 2 lcores\n");
+		return TEST_SKIPPED;
+	}
+
+	return 0;
+}
+
+static void
+graph_perf_teardown(void)
+{
+}
+
+static inline rte_node_t
+graph_node_get(const char *pname, char *nname)
+{
+	rte_node_t pnode_id = rte_node_from_name(pname);
+	char lookup_name[RTE_NODE_NAMESIZE];
+	rte_node_t node_id;
+
+	snprintf(lookup_name, RTE_NODE_NAMESIZE, "%s-%s", pname, nname);
+	node_id = rte_node_from_name(lookup_name);
+
+	if (node_id != RTE_NODE_ID_INVALID) {
+		if (rte_node_edge_count(node_id))
+			rte_node_edge_shrink(node_id, 0);
+		return node_id;
+	}
+
+	return rte_node_clone(pnode_id, nname);
+}
+
+static uint16_t
+graph_node_count_edges(uint32_t stage, uint16_t node, uint16_t nodes_per_stage,
+		       uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+		       char *ename[], struct test_node_data *node_data,
+		       rte_node_t **node_map)
+{
+	uint8_t total_percent = 0;
+	uint16_t edges = 0;
+	int i;
+
+	for (i = 0; i < nodes_per_stage && edges < MAX_EDGES_PER_NODE; i++) {
+		if (edge_map[stage + 1][i][node]) {
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[stage + 1][i]));
+			node_data->next_nodes[edges] = node_map[stage + 1][i];
+			node_data->next_percentage[edges] =
+				edge_map[stage + 1][i][node];
+			edges++;
+			total_percent += edge_map[stage + 1][i][node];
+		}
+	}
+
+	if (edges >= MAX_EDGES_PER_NODE || (edges && total_percent != 100)) {
+		for (i = 0; i < edges; i++)
+			free(ename[i]);
+		return RTE_EDGE_ID_INVALID;
+	}
+
+	return edges;
+}
+
+static int
+graph_init(const char *gname, uint8_t nb_srcs, uint8_t nb_sinks,
+	   uint32_t stages, uint16_t nodes_per_stage,
+	   uint8_t src_map[][nodes_per_stage], uint8_t snk_map[][nb_sinks],
+	   uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+	   uint8_t burst_one)
+{
+	struct test_graph_perf *graph_data;
+	char nname[RTE_NODE_NAMESIZE / 2];
+	struct test_node_data *node_data;
+	char *ename[nodes_per_stage];
+	struct rte_graph_param gconf;
+	const struct rte_memzone *mz;
+	uint8_t total_percent = 0;
+	rte_node_t *src_nodes;
+	rte_node_t *snk_nodes;
+	rte_node_t **node_map;
+	char **node_patterns;
+	rte_graph_t graph_id;
+	rte_edge_t edges;
+	rte_edge_t count;
+	uint32_t i, j, k;
+
+	mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
+				 sizeof(struct test_graph_perf), 0, 0);
+	if (mz == NULL) {
+		printf("Failed to allocate graph common memory\n");
+		return -ENOMEM;
+	}
+
+	graph_data = mz->addr;
+	graph_data->nb_nodes = 0;
+	graph_data->node_data =
+		malloc(sizeof(struct test_node_data) *
+		       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (graph_data->node_data == NULL) {
+		printf("Failed to reserve memzone for graph data\n");
+		goto memzone_free;
+	}
+
+	node_patterns = malloc(sizeof(char *) *
+			       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (node_patterns == NULL) {
+		printf("Failed to reserve memory for node patterns\n");
+		goto data_free;
+	}
+
+	src_nodes = malloc(sizeof(rte_node_t) * nb_srcs);
+	if (src_nodes == NULL) {
+		printf("Failed to reserve memory for src nodes\n");
+		goto pattern_free;
+	}
+
+	snk_nodes = malloc(sizeof(rte_node_t) * nb_sinks);
+	if (snk_nodes == NULL) {
+		printf("Failed to reserve memory for snk nodes\n");
+		goto src_free;
+	}
+
+	node_map = malloc(sizeof(rte_node_t *) * stages +
+			  sizeof(rte_node_t) * nodes_per_stage * stages);
+	if (node_map == NULL) {
+		printf("Failed to reserve memory for node map\n");
+		goto snk_free;
+	}
+
+	/* Setup the Graph */
+	for (i = 0; i < stages; i++) {
+		node_map[i] =
+			(rte_node_t *)(node_map + stages) + nodes_per_stage * i;
+		for (j = 0; j < nodes_per_stage; j++) {
+			total_percent = 0;
+			for (k = 0; k < nodes_per_stage; k++)
+				total_percent += edge_map[i][j][k];
+			if (!total_percent)
+				continue;
+			node_patterns[graph_data->nb_nodes] =
+				malloc(RTE_NODE_NAMESIZE);
+			if (node_patterns[graph_data->nb_nodes] == NULL) {
+				printf("Failed to create memory for pattern\n");
+				goto pattern_name_free;
+			}
+
+			/* Clone a worker node */
+			snprintf(nname, sizeof(nname), "%d-%d", i, j);
+			node_map[i][j] =
+				graph_node_get(TEST_GRAPH_WRK_NAME, nname);
+			if (node_map[i][j] == RTE_NODE_ID_INVALID) {
+				printf("Failed to create node[%s]\n", nname);
+				graph_data->nb_nodes++;
+				goto pattern_name_free;
+			}
+			snprintf(node_patterns[graph_data->nb_nodes],
+				 RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[i][j]));
+			node_data =
+				&graph_data->node_data[graph_data->nb_nodes];
+			node_data->node_id = node_map[i][j];
+			node_data->is_sink = false;
+			graph_data->nb_nodes++;
+		}
+	}
+
+	for (i = 0; i < stages - 1; i++) {
+		for (j = 0; j < nodes_per_stage; j++) {
+			/* Count edges i.e connections of worker node to next */
+			node_data =
+				graph_get_node_data(graph_data, node_map[i][j]);
+			edges = graph_node_count_edges(i, j, nodes_per_stage,
+						       edge_map, ename,
+						       node_data, node_map);
+			if (edges == RTE_EDGE_ID_INVALID) {
+				printf("Invalid edge configuration\n");
+				goto pattern_name_free;
+			}
+			if (!edges)
+				continue;
+
+			/* Connect a node in stage 'i' to nodes
+			 * in stage 'i + 1' with edges.
+			 */
+			count = rte_node_edge_update(
+				node_map[i][j], 0,
+				(const char **)(uintptr_t)ename, edges);
+			for (k = 0; k < edges; k++)
+				free(ename[k]);
+			if (count != edges) {
+				printf("Couldn't add edges %d %d\n", edges,
+				       count);
+				goto pattern_name_free;
+			}
+		}
+	}
+
+	/* Setup Source nodes */
+	for (i = 0; i < nb_srcs; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+		/* Clone a source node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		src_nodes[i] =
+			graph_node_get(burst_one ? TEST_GRAPH_SRC_BRST_ONE_NAME
+						 : TEST_GRAPH_SRC_NAME,
+				       nname);
+		if (src_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(src_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = src_nodes[i];
+		node_data->is_sink = false;
+		graph_data->nb_nodes++;
+
+		/* Prepare next node list  to connect to */
+		for (j = 0; j < nodes_per_stage; j++) {
+			if (!src_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[0][j]));
+			node_data->next_nodes[edges] = node_map[0][j];
+			node_data->next_percentage[edges] = src_map[i][j];
+			edges++;
+			total_percent += src_map[i][j];
+		}
+
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[j]);
+			goto pattern_name_free;
+		}
+
+		/* Connect to list of next nodes using edges */
+		count = rte_node_edge_update(src_nodes[i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Setup Sink nodes */
+	for (i = 0; i < nb_sinks; i++) {
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+
+		/* Clone a sink node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		snk_nodes[i] = graph_node_get(TEST_GRAPH_SNK_NAME, nname);
+		if (snk_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(snk_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = snk_nodes[i];
+		node_data->is_sink = true;
+		graph_data->nb_nodes++;
+	}
+
+	/* Connect last stage worker nodes to sink nodes */
+	for (i = 0; i < nodes_per_stage; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_data = graph_get_node_data(graph_data,
+						node_map[stages - 1][i]);
+		/* Prepare list of sink nodes to connect to */
+		for (j = 0; j < nb_sinks; j++) {
+			if (!snk_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(snk_nodes[j]));
+			node_data->next_nodes[edges] = snk_nodes[j];
+			node_data->next_percentage[edges] = snk_map[i][j];
+			edges++;
+			total_percent += snk_map[i][j];
+		}
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[i]);
+			goto pattern_name_free;
+		}
+
+		/* Connect a worker node to a list of sink nodes */
+		count = rte_node_edge_update(node_map[stages - 1][i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Create a Graph */
+	gconf.socket_id = SOCKET_ID_ANY;
+	gconf.nb_node_patterns = graph_data->nb_nodes;
+	gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
+
+	graph_id = rte_graph_create(gname, &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		goto pattern_name_free;
+	}
+	graph_data->graph_id = graph_id;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+	free(snk_nodes);
+	free(src_nodes);
+	free(node_patterns);
+	return 0;
+
+pattern_name_free:
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+snk_free:
+	free(snk_nodes);
+src_free:
+	free(src_nodes);
+pattern_free:
+	free(node_patterns);
+data_free:
+	free(graph_data->node_data);
+memzone_free:
+	rte_memzone_free(mz);
+	return -ENOMEM;
+}
+
+/* Worker thread function */
+static int
+_graph_perf_wrapper(void *args)
+{
+	struct graph_lcore_data *data = args;
+	struct rte_graph *graph;
+
+	/* Lookup graph */
+	graph = rte_graph_lookup(rte_graph_id_to_name(data->graph_id));
+
+	/* Graph walk until done */
+	while (!data->done)
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
+static int
+measure_perf_get(rte_graph_t graph_id)
+{
+	const char *pattern = rte_graph_id_to_name(graph_id);
+	uint32_t lcore_id = rte_get_next_lcore(-1, 1, 0);
+	struct rte_graph_cluster_stats_param param;
+	struct rte_graph_cluster_stats *stats;
+	struct graph_lcore_data *data;
+
+	data = rte_zmalloc("Graph_perf", sizeof(struct graph_lcore_data),
+			   RTE_CACHE_LINE_SIZE);
+	data->graph_id = graph_id;
+	data->done = 0;
+
+	/* Run graph worker thread function */
+	rte_eal_remote_launch(_graph_perf_wrapper, data, lcore_id);
+
+	/* Collect stats for few msecs */
+	if (rte_graph_has_stats_feature()) {
+		memset(&param, 0, sizeof(param));
+		param.f = stdout;
+		param.socket_id = SOCKET_ID_ANY;
+		param.graph_patterns = &pattern;
+		param.nb_graph_patterns = 1;
+
+		stats = rte_graph_cluster_stats_create(&param);
+		if (stats == NULL) {
+			printf("Failed to create stats\n");
+			return -ENOMEM;
+		}
+
+		rte_delay_ms(3E2);
+		rte_graph_cluster_stats_get(stats, true);
+		rte_delay_ms(1E3);
+		rte_graph_cluster_stats_get(stats, false);
+		rte_graph_cluster_stats_destroy(stats);
+	} else
+		rte_delay_ms(1E3);
+
+	data->done = 1;
+	rte_eal_wait_lcore(lcore_id);
+
+	return 0;
+}
+
+static inline void
+graph_fini(void)
+{
+	const struct rte_memzone *mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	struct test_graph_perf *graph_data;
+
+	if (mz == NULL)
+		return;
+	graph_data = mz->addr;
+
+	rte_graph_destroy(rte_graph_id_to_name(graph_data->graph_id));
+	free(graph_data->node_data);
+	rte_memzone_free(rte_memzone_lookup(TEST_GRAPH_PERF_MZ));
+}
+
+static int
+measure_perf(void)
+{
+	const struct rte_memzone *mz;
+	struct test_graph_perf *graph_data;
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+
+	return measure_perf_get(graph_data->graph_id);
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk_brst_one(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_2src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_2snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_tree_4s_4n_1src_4snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_reverse_tree_3s_4n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_parallel_tree_5s_4n_4src_4snk(void)
+{
+	return measure_perf();
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr_brst_one(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 1);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			2
+ * sink:		1
+ */
+static inline int
+graph_init_hr_multi_src(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = {
+		{100}, {100}
+	};
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		2
+ */
+static inline int
+graph_init_hr_multi_snk(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][2] = { {50, 50} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		4
+ * src:			1
+ * sink:		4
+ */
+static inline int
+graph_init_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 0, 0, 0},
+			{50, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{33, 33, 0, 0},
+			{34, 34, 0, 0},
+			{33, 33, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0}
+		}
+	};
+	uint8_t src_map[][4] = { {100, 0, 0, 0} };
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		3
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_reverse_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25}
+		},
+		{
+			{33, 33, 33, 33},
+			{33, 33, 33, 33},
+			{34, 34, 34, 34},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 50, 50, 0},
+			{50, 50, 50, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+	};
+	uint8_t src_map[][4] = { {25, 25, 25, 25} };
+	uint8_t snk_map[][1] = { {100}, {100}, {0}, {0} };
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		5
+ * src:			4
+ * sink:		4
+ */
+static inline int
+graph_init_parallel_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+	};
+	uint8_t src_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_parallel", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/** Graph Creation cheat sheet
+ *  edge_map -> dictates graph flow from worker stage 0 to worker stage n-1.
+ *  src_map  -> dictates source nodes enqueue percentage to worker stage 0.
+ *  snk_map  -> dictates stage n-1 enqueue percentage to sink.
+ *
+ *  Layout:
+ *  edge_map[<nb_stages>][<nodes_per_stg>][<nodes_in_nxt_stg = nodes_per_stg>]
+ *  src_map[<nb_sources>][<nodes_in_stage0 = nodes_per_stage>]
+ *  snk_map[<nodes_in_stage(n-1) = nodes_per_stage>][<nb_sinks>]
+ *
+ *  The last array dictates the percentage of received objs to enqueue to next
+ *  stage.
+ *
+ *  Note: edge_map[][0][] will always be unused as it will receive from source
+ *
+ *  Example:
+ *	Graph:
+ *	http://bit.ly/2PqbqOy
+ *	Each stage(n) connects to all nodes in the next stage in decreasing
+ *	order.
+ *	Since we can't resize the edge_map dynamically we get away by creating
+ *	dummy nodes and assigning 0 percentages.
+ *	Max nodes across all stages = 4
+ *	stages = 3
+ *	nb_src = 1
+ *	nb_snk = 1
+ *			   // Stages
+ *	edge_map[][4][4] = {
+ *		// Nodes per stage
+ *		{
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25}
+ *		},	// This will be unused.
+ *		{
+ *		    // Nodes enabled in current stage + prev stage enq %
+ *		    {33, 33, 33, 33},
+ *		    {33, 33, 33, 33},
+ *		    {34, 34, 34, 34},
+ *		    {0, 0, 0, 0}
+ *		},
+ *		{
+ *		    {50, 50, 50, 0},
+ *		    {50, 50, 50, 0},
+ *		    {0, 0, 0, 0},
+ *		    {0, 0, 0, 0}
+ *		},
+ *	};
+ *	Above, each stage tells how much it should receive from previous except
+ *	from stage_0.
+ *
+ *	src_map[][4] = { {25, 25, 25, 25} };
+ *	Here, we tell each source the % it has to send to stage_0 nodes. In
+ *	case we want 2 source node we can declare as
+ *	src_map[][4] = { {25, 25, 25, 25}, {25, 25, 25, 25} };
+ *
+ *	snk_map[][1] = { {100}, {100}, {0}, {0} }
+ *	Here, we tell stage - 1 nodes how much to enqueue to sink_0.
+ *	If we have 2 sinks we can do as follows
+ *	snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
+ */
+
+static struct unit_test_suite graph_perf_testsuite = {
+	.suite_name = "Graph library performance test suite",
+	.setup = graph_perf_setup,
+	.teardown = graph_perf_teardown,
+	.unit_test_cases = {
+		TEST_CASE_ST(graph_init_hr, graph_fini,
+			     graph_hr_4s_1n_1src_1snk),
+		TEST_CASE_ST(graph_init_hr_brst_one, graph_fini,
+			     graph_hr_4s_1n_1src_1snk_brst_one),
+		TEST_CASE_ST(graph_init_hr_multi_src, graph_fini,
+			     graph_hr_4s_1n_2src_1snk),
+		TEST_CASE_ST(graph_init_hr_multi_snk, graph_fini,
+			     graph_hr_4s_1n_1src_2snk),
+		TEST_CASE_ST(graph_init_tree, graph_fini,
+			     graph_tree_4s_4n_1src_4snk),
+		TEST_CASE_ST(graph_init_reverse_tree, graph_fini,
+			     graph_reverse_tree_3s_4n_1src_1snk),
+		TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
+			     graph_parallel_tree_5s_4n_4src_4snk),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+test_graph_perf_func(void)
+{
+	return unit_test_suite_runner(&graph_perf_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_perf_autotest, test_graph_perf_func);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 15/29] node: add log infra and null node
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (13 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 14/29] graph: add performance testcase jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 16/29] node: add ethdev Rx node jerinj
                       ` (14 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic,
	Nithin Dabilpuram, Pavan Nikhilesh, Bruce Richardson
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add log infra for node specific logging.
Also, add null rte_node that just ignores all the objects
directed to it.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 MAINTAINERS                          |  5 +++++
 app/test/meson.build                 |  7 +++++--
 config/common_base                   |  5 +++++
 doc/api/doxy-api.conf.in             |  1 +
 lib/Makefile                         |  3 +++
 lib/librte_node/Makefile             | 22 ++++++++++++++++++++++
 lib/librte_node/log.c                | 14 ++++++++++++++
 lib/librte_node/meson.build          |  8 ++++++++
 lib/librte_node/node_private.h       | 22 ++++++++++++++++++++++
 lib/librte_node/null.c               | 23 +++++++++++++++++++++++
 lib/librte_node/rte_node_version.map |  6 ++++++
 lib/meson.build                      |  5 ++++-
 meson.build                          |  1 +
 mk/rte.app.mk                        |  1 +
 14 files changed, 120 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/rte_node_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index bc7085983..c1acaedab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1474,6 +1474,11 @@ M: Jerin Jacob <jerinj@marvell.com>
 M: Kiran Kumar K <kirankumark@marvell.com>
 F: lib/librte_graph/
 
+Nodes - EXPERIMENTAL
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+M: Pavan Nikhilesh <pbhagavatula@marvell.com>
+F: lib/librte_node/
+
 
 Test Applications
 -----------------
diff --git a/app/test/meson.build b/app/test/meson.build
index 9006cc074..728d20c1f 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -154,7 +154,8 @@ test_deps = ['acl',
 	'ring',
 	'stack',
 	'timer',
-	'graph'
+	'graph',
+	'node'
 ]
 
 # Each test is marked with flag true/false
@@ -390,13 +391,15 @@ endforeach
 test_dep_objs += cc.find_library('execinfo', required: false)
 
 link_libs = []
+link_nodes = []
 if get_option('default_library') == 'static'
 	link_libs = dpdk_drivers
+	link_nodes = dpdk_graph_nodes
 endif
 
 dpdk_test = executable('dpdk-test',
 	test_sources,
-	link_whole: link_libs,
+	link_whole: link_libs + link_nodes,
 	dependencies: test_dep_objs,
 	c_args: [cflags, '-DALLOW_EXPERIMENTAL_API'],
 	install_rpath: driver_install_path,
diff --git a/config/common_base b/config/common_base
index 32f982136..1ed5187dc 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1081,6 +1081,11 @@ CONFIG_RTE_LIBRTE_GRAPH=y
 CONFIG_RTE_GRAPH_BURST_SIZE=256
 CONFIG_RTE_LIBRTE_GRAPH_STATS=y
 
+#
+# Compile librte_node
+#
+CONFIG_RTE_LIBRTE_NODE=y
+
 #
 # Compile the test application
 #
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 759a7213e..1d4f1a37d 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -49,6 +49,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_mempool \
                           @TOPDIR@/lib/librte_meter \
                           @TOPDIR@/lib/librte_metrics \
+                          @TOPDIR@/lib/librte_node \
                           @TOPDIR@/lib/librte_net \
                           @TOPDIR@/lib/librte_pci \
                           @TOPDIR@/lib/librte_pdump \
diff --git a/lib/Makefile b/lib/Makefile
index 1f572b659..50d61a338 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -122,6 +122,9 @@ DEPDIRS-librte_rcu := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
 DEPDIRS-librte_graph := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_NODE) += librte_node
+DEPDIRS-librte_node := librte_graph librte_lpm librte_ethdev librte_mbuf
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
new file mode 100644
index 000000000..dbc8e1d44
--- /dev/null
+++ b/lib/librte_node/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_node.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
+LDLIBS += -lrte_eal -lrte_graph
+
+EXPORT_MAP := rte_node_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/log.c b/lib/librte_node/log.c
new file mode 100644
index 000000000..f035f91e8
--- /dev/null
+++ b/lib/librte_node/log.c
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "node_private.h"
+
+int rte_node_logtype;
+
+RTE_INIT(rte_node_init_log)
+{
+	rte_node_logtype = rte_log_register("lib.node");
+	if (rte_node_logtype >= 0)
+		rte_log_set_level(rte_node_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
new file mode 100644
index 000000000..a97813ad4
--- /dev/null
+++ b/lib/librte_node/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+sources = files('null.c', 'log.c')
+allow_experimental_apis = true
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
+deps += ['graph']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
new file mode 100644
index 000000000..f30902a94
--- /dev/null
+++ b/lib/librte_node/node_private.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __NODE_PRIVATE_H__
+#define __NODE_PRIVATE_H__
+
+#include <rte_common.h>
+#include <rte_log.h>
+
+extern int rte_node_logtype;
+#define NODE_LOG(level, node_name, ...)                                        \
+	rte_log(RTE_LOG_##level, rte_node_logtype,                             \
+		RTE_FMT("NODE %s: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",  \
+			node_name, __func__, __LINE__,                         \
+			RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define node_err(node_name, ...) NODE_LOG(ERR, node_name, __VA_ARGS__)
+#define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
+#define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
+
+#endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/null.c b/lib/librte_node/null.c
new file mode 100644
index 000000000..c7cd8b6df
--- /dev/null
+++ b/lib/librte_node/null.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_graph.h>
+
+static uint16_t
+null(struct rte_graph *graph, struct rte_node *node, void **objs,
+	uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(graph);
+
+	return nb_objs;
+}
+
+static struct rte_node_register null_node = {
+	.name = "null",
+	.process = null,
+};
+
+RTE_NODE_REGISTER(null_node);
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
new file mode 100644
index 000000000..f87163bb9
--- /dev/null
+++ b/lib/librte_node/rte_node_version.map
@@ -0,0 +1,6 @@
+EXPERIMENTAL {
+	global:
+
+	rte_node_logtype;
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index c43d86bb9..147129b0b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'graph', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'node', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
@@ -186,6 +186,9 @@ foreach l:libraries
 
 			dpdk_libraries = [shared_lib] + dpdk_libraries
 			dpdk_static_libraries = [static_lib] + dpdk_static_libraries
+			if libname == 'rte_node'
+				dpdk_graph_nodes = [static_lib]
+			endif
 		endif # sources.length() > 0
 
 		set_variable('shared_rte_' + name, shared_dep)
diff --git a/meson.build b/meson.build
index b7ae9c8d9..811c96421 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@ cc = meson.get_compiler('c')
 dpdk_conf = configuration_data()
 dpdk_libraries = []
 dpdk_static_libraries = []
+dpdk_graph_nodes = []
 dpdk_driver_classes = []
 dpdk_drivers = []
 dpdk_extra_ldflags = []
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index b1195f09a..68d7806a4 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -99,6 +99,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
 _LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
+_LDLIBS-$(CONFIG_RTE_LIBRTE_NODE)           += -lrte_node
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 16/29] node: add ethdev Rx node
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (14 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 15/29] node: add log infra and null node jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 17/29] node: add ethdev Tx node jerinj
                       ` (13 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add source rte_node ethdev_rx process function and register
it. This node is a source node that will be called periodically
and when called, performs rte_eth_rx_burst() on a specific
(port, queue) pair and enqueue them as stream of objects to
next node.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |   3 +-
 lib/librte_node/ethdev_rx.c      | 221 +++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_rx_priv.h |  81 +++++++++++
 lib/librte_node/meson.build      |   4 +-
 4 files changed, 306 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index dbc8e1d44..314149385 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,12 +11,13 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph
+LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_rx.c b/lib/librte_node/ethdev_rx.c
new file mode 100644
index 000000000..5cc736598
--- /dev/null
+++ b/lib/librte_node/ethdev_rx.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_rx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_rx_node_main ethdev_rx_main;
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process_inline(struct rte_graph *graph, struct rte_node *node,
+			      uint16_t port, uint16_t queue)
+{
+	uint16_t count, next_index = ETHDEV_RX_NEXT_IP4_LOOKUP;
+
+	/* Get pkts from port */
+	count = rte_eth_rx_burst(port, queue, (struct rte_mbuf **)node->objs,
+				 RTE_GRAPH_BURST_SIZE);
+
+	if (!count)
+		return 0;
+	node->idx = count;
+	/* Enqueue to next node */
+	rte_node_next_stream_move(graph, node, next_index);
+
+	return count;
+}
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t cnt)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	uint16_t n_pkts = 0;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(cnt);
+
+	n_pkts = ethdev_rx_node_process_inline(graph, node, ctx->port_id,
+					       ctx->queue_id);
+	return n_pkts;
+}
+
+static inline uint32_t
+l3_ptype(uint16_t etype, uint32_t ptype)
+{
+	ptype = ptype & ~RTE_PTYPE_L3_MASK;
+	if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
+		ptype |= RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
+	else if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6))
+		ptype |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	return ptype;
+}
+
+/* Callback for soft ptype parsing */
+static uint16_t
+eth_pkt_parse_cb(uint16_t port, uint16_t queue, struct rte_mbuf **mbufs,
+		 uint16_t nb_pkts, uint16_t max_pkts, void *user_param)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3;
+	struct rte_ether_hdr *eth_hdr;
+	uint16_t etype, n_left;
+	struct rte_mbuf **pkts;
+
+	RTE_SET_USED(port);
+	RTE_SET_USED(queue);
+	RTE_SET_USED(max_pkts);
+	RTE_SET_USED(user_param);
+
+	pkts = mbufs;
+	n_left = nb_pkts;
+	while (n_left >= 12) {
+
+		/* Prefetch next-next mbufs */
+		rte_prefetch0(pkts[8]);
+		rte_prefetch0(pkts[9]);
+		rte_prefetch0(pkts[10]);
+		rte_prefetch0(pkts[11]);
+
+		/* Prefetch next mbuf data */
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[4], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[5], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[6], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[7], struct rte_ether_hdr *));
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+		pkts += 4;
+		n_left -= 4;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf1 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf1->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf2 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf2->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf3 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf3->packet_type = l3_ptype(etype, 0);
+	}
+
+	while (n_left > 0) {
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left -= 1;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+	}
+
+	return nb_pkts;
+}
+
+#define MAX_PTYPES 16
+static int
+ethdev_ptype_setup(uint16_t port, uint16_t queue)
+{
+	uint8_t l3_ipv4 = 0, l3_ipv6 = 0;
+	uint32_t ptypes[MAX_PTYPES];
+	int i, rc;
+
+	/* Check IPv4 & IPv6 ptype support */
+	rc = rte_eth_dev_get_supported_ptypes(port, RTE_PTYPE_L3_MASK, ptypes,
+					      MAX_PTYPES);
+	for (i = 0; i < rc; i++) {
+		if (ptypes[i] & RTE_PTYPE_L3_IPV4)
+			l3_ipv4 = 1;
+		if (ptypes[i] & RTE_PTYPE_L3_IPV6)
+			l3_ipv6 = 1;
+	}
+
+	if (!l3_ipv4 || !l3_ipv6) {
+		node_info("ethdev_rx",
+			  "Enabling ptype callback for required ptypes on port %u\n",
+			  port);
+
+		if (!rte_eth_add_rx_callback(port, queue, eth_pkt_parse_cb,
+					     NULL)) {
+			node_err("ethdev_rx",
+				 "Failed to add rx ptype cb: port=%d, queue=%d\n",
+				 port, queue);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int
+ethdev_rx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	ethdev_rx_node_elem_t *elem = ethdev_rx_main.head;
+
+	RTE_SET_USED(graph);
+
+	while (elem) {
+		if (elem->nid == node->id) {
+			/* Update node specific context */
+			memcpy(ctx, &elem->ctx, sizeof(ethdev_rx_node_ctx_t));
+			break;
+		}
+		elem = elem->next;
+	}
+
+	RTE_VERIFY(elem != NULL);
+
+	/* Check and setup ptype */
+	return ethdev_ptype_setup(ctx->port_id, ctx->queue_id);
+}
+
+struct ethdev_rx_node_main *
+ethdev_rx_get_node_data_get(void)
+{
+	return &ethdev_rx_main;
+}
+
+static struct rte_node_register ethdev_rx_node_base = {
+	.process = ethdev_rx_node_process,
+	.flags = RTE_NODE_SOURCE_F,
+	.name = "ethdev_rx",
+
+	.init = ethdev_rx_node_init,
+
+	.nb_edges = ETHDEV_RX_NEXT_MAX,
+	.next_nodes = {[ETHDEV_RX_NEXT_IP4_LOOKUP] = "ip4_lookup"},
+};
+
+struct rte_node_register *
+ethdev_rx_node_get(void)
+{
+	return &ethdev_rx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_rx_node_base);
diff --git a/lib/librte_node/ethdev_rx_priv.h b/lib/librte_node/ethdev_rx_priv.h
new file mode 100644
index 000000000..2d7195a36
--- /dev/null
+++ b/lib/librte_node/ethdev_rx_priv.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_RX_PRIV_H__
+#define __INCLUDE_ETHDEV_RX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+struct ethdev_rx_node_elem;
+struct ethdev_rx_node_ctx;
+typedef struct ethdev_rx_node_elem ethdev_rx_node_elem_t;
+typedef struct ethdev_rx_node_ctx ethdev_rx_node_ctx_t;
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node context structure.
+ */
+struct ethdev_rx_node_ctx {
+	uint16_t port_id;  /**< Port identifier of the Rx node. */
+	uint16_t queue_id; /**< Queue identifier of the Rx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node list element structure.
+ */
+struct ethdev_rx_node_elem {
+	struct ethdev_rx_node_elem *next;
+	/**< Pointer to the next Rx node element. */
+	struct ethdev_rx_node_ctx ctx;
+	/**< Rx node context. */
+	rte_node_t nid;
+	/**< Node identifier of the Rx node. */
+};
+
+enum ethdev_rx_next_nodes {
+	ETHDEV_RX_NEXT_IP4_LOOKUP,
+	ETHDEV_RX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Rx node main structure.
+ */
+struct ethdev_rx_node_main {
+	ethdev_rx_node_elem_t *head;
+	/**< Pointer to the head Rx node element. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Rx node data.
+ */
+struct ethdev_rx_node_main *ethdev_rx_get_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Rx node.
+ */
+struct rte_node_register *ethdev_rx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_RX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index a97813ad4..94caa6c23 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph']
+deps += ['graph', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 17/29] node: add ethdev Tx node
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (15 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 16/29] node: add ethdev Rx node jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
                       ` (12 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add rte_node ethdev_tx process function and register it to
graph infra. This node has a specific (port, tx-queue) as context
and it enqueue's all the packets received to that specific queue pair.
When rte_eth_tx_burst() i.e enqueue to queue pair fails, packets
are forwarded to pkt_drop node to be free'd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |  3 +-
 lib/librte_node/ethdev_tx.c      | 86 ++++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_tx_priv.h | 62 +++++++++++++++++++++++
 lib/librte_node/meson.build      |  4 +-
 4 files changed, 152 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 314149385..7428f6c43 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
@@ -19,5 +19,6 @@ EXPORT_MAP := rte_node_version.map
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_tx.c b/lib/librte_node/ethdev_tx.c
new file mode 100644
index 000000000..075149089
--- /dev/null
+++ b/lib/librte_node/ethdev_tx.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_tx_priv.h"
+
+static struct ethdev_tx_node_main ethdev_tx_main;
+
+static uint16_t
+ethdev_tx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t nb_objs)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint16_t port, queue;
+	uint16_t count;
+
+	/* Get Tx port id */
+	port = ctx->port;
+	queue = ctx->queue;
+
+	count = rte_eth_tx_burst(port, queue, (struct rte_mbuf **)objs,
+				 nb_objs);
+
+	/* Redirect unsent pkts to drop node */
+	if (count != nb_objs) {
+		rte_node_enqueue(graph, node, ETHDEV_TX_NEXT_PKT_DROP,
+				 &objs[count], nb_objs - count);
+	}
+
+	return count;
+}
+
+static int
+ethdev_tx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint64_t port_id = RTE_MAX_ETHPORTS;
+	int i;
+
+	/* Find our port id */
+	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+		if (ethdev_tx_main.nodes[i] == node->id) {
+			port_id = i;
+			break;
+		}
+	}
+	RTE_VERIFY(port_id < RTE_MAX_ETHPORTS);
+
+	/* Update port and queue */
+	ctx->port = port_id;
+	ctx->queue = graph->id;
+
+	return 0;
+}
+
+struct ethdev_tx_node_main *
+ethdev_tx_node_data_get(void)
+{
+	return &ethdev_tx_main;
+}
+
+static struct rte_node_register ethdev_tx_node_base = {
+	.process = ethdev_tx_node_process,
+	.name = "ethdev_tx",
+
+	.init = ethdev_tx_node_init,
+
+	.nb_edges = ETHDEV_TX_NEXT_MAX,
+	.next_nodes = {
+		[ETHDEV_TX_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+struct rte_node_register *
+ethdev_tx_node_get(void)
+{
+	return &ethdev_tx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_tx_node_base);
diff --git a/lib/librte_node/ethdev_tx_priv.h b/lib/librte_node/ethdev_tx_priv.h
new file mode 100644
index 000000000..586bff44a
--- /dev/null
+++ b/lib/librte_node/ethdev_tx_priv.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_TX_PRIV_H__
+#define __INCLUDE_ETHDEV_TX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ethdev_tx_node_ctx;
+typedef struct ethdev_tx_node_ctx ethdev_tx_node_ctx_t;
+
+enum ethdev_tx_next_nodes {
+	ETHDEV_TX_NEXT_PKT_DROP,
+	ETHDEV_TX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node context structure.
+ */
+struct ethdev_tx_node_ctx {
+	uint16_t port;	/**< Port identifier of the Ethernet Tx node. */
+	uint16_t queue; /**< Queue identifier of the Ethernet Tx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node main structure.
+ */
+struct ethdev_tx_node_main {
+	uint32_t nodes[RTE_MAX_ETHPORTS]; /**< Tx nodes for each ethdev port. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Tx node data.
+ */
+struct ethdev_tx_node_main *ethdev_tx_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Tx node.
+ */
+struct rte_node_register *ethdev_tx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_TX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 94caa6c23..505c76abd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'ethdev']
+deps += ['graph', 'mbuf', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (16 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 17/29] node: add ethdev Tx node jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 19/29] node: add generic ipv4 lookup node jerinj
                       ` (11 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ctrl api to setup ethdev_rx and ethdev_tx node.
This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
nodes with specific (port, queue) pairs updated in their context.
All the ethdev ports and queues are setup before this api
is called.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 doc/api/doxy-api-index.md            |   2 +
 lib/librte_node/Makefile             |   6 +-
 lib/librte_node/ethdev_ctrl.c        | 100 +++++++++++++++++++++++++++
 lib/librte_node/meson.build          |   5 +-
 lib/librte_node/node_private.h       |  74 ++++++++++++++++++++
 lib/librte_node/rte_node_eth_api.h   |  70 +++++++++++++++++++
 lib/librte_node/rte_node_version.map |   1 +
 7 files changed, 255 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index fd2ff64d7..1784674df 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -161,6 +161,8 @@ The public API headers are grouped by topics:
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
     [graph_worker]     (@ref rte_graph_worker.h)
+  * graph_nodes:
+    [eth_node]         (@ref rte_node_eth_api.h),
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 7428f6c43..ea5fa77f7 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -20,5 +20,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
new file mode 100644
index 000000000..b2ac5e2c4
--- /dev/null
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+
+#include "rte_node_eth_api.h"
+
+#include "ethdev_rx_priv.h"
+#include "ethdev_tx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_ctrl {
+	uint16_t nb_graphs;
+} ctrl;
+
+int
+rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
+		    uint16_t nb_graphs)
+{
+	struct ethdev_tx_node_main *tx_node_data;
+	uint16_t tx_q_used, rx_q_used, port_id;
+	struct rte_node_register *tx_node;
+	char name[RTE_NODE_NAMESIZE];
+	struct rte_mempool *mp;
+	uint32_t id;
+	int i, j;
+
+	tx_node_data = ethdev_tx_node_data_get();
+	tx_node = ethdev_tx_node_get();
+	for (i = 0; i < nb_confs; i++) {
+		port_id = conf[i].port_id;
+
+		if (!rte_eth_dev_is_valid_port(port_id))
+			return -EINVAL;
+
+		/* Check for mbuf minimum private size requirement */
+		for (j = 0; j < conf[i].mp_count; j++) {
+			mp = conf[i].mp[j];
+			if (!mp)
+				continue;
+			/* Check for minimum private space */
+			if (rte_pktmbuf_priv_size(mp) <
+			    RTE_NODE_MBUF_PRIV2_SIZE) {
+				node_err("ethdev",
+					 "Minimum mbuf priv size requirement not met by mp %s",
+					 mp->name);
+				return -EINVAL;
+			}
+		}
+
+		rx_q_used = conf[i].num_rx_queues;
+		tx_q_used = conf[i].num_tx_queues;
+		/* Check if we have a txq for each worker */
+		if (tx_q_used < nb_graphs)
+			return -EINVAL;
+
+		/* Create node for each rx port queue pair */
+		for (j = 0; j < rx_q_used; j++) {
+			struct ethdev_rx_node_main *rx_node_data;
+			struct rte_node_register *rx_node;
+			ethdev_rx_node_elem_t *elem;
+
+			rx_node_data = ethdev_rx_get_node_data_get();
+			rx_node = ethdev_rx_node_get();
+			snprintf(name, sizeof(name), "%u-%u", port_id, j);
+			/* Clone a new rx node with same edges as parent */
+			id = rte_node_clone(rx_node->id, name);
+			if (id == RTE_NODE_ID_INVALID)
+				return -EIO;
+
+			/* Add it to list of ethdev rx nodes for lookup */
+			elem = malloc(sizeof(ethdev_rx_node_elem_t));
+			memset(elem, 0, sizeof(ethdev_rx_node_elem_t));
+			elem->ctx.port_id = port_id;
+			elem->ctx.queue_id = j;
+			elem->nid = id;
+			elem->next = rx_node_data->head;
+			rx_node_data->head = elem;
+
+			node_dbg("ethdev", "Rx node %s-%s: is at %u",
+				 rx_node->name, name, id);
+		}
+
+		/* Create a per port tx node from base node */
+		snprintf(name, sizeof(name), "%u", port_id);
+		/* Clone a new node with same edges as parent */
+		id = rte_node_clone(tx_node->id, name);
+		tx_node_data->nodes[port_id] = id;
+
+		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
+			 name, id);
+	}
+
+	ctrl.nb_graphs = nb_graphs;
+	return 0;
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 505c76abd..af2eb2d91 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
+headers = files('rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'mbuf', 'ethdev']
+deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
index f30902a94..141448546 100644
--- a/lib/librte_node/node_private.h
+++ b/lib/librte_node/node_private.h
@@ -6,7 +6,9 @@
 #define __NODE_PRIVATE_H__
 
 #include <rte_common.h>
+#include <rte_crypto.h>
 #include <rte_log.h>
+#include <rte_mbuf.h>
 
 extern int rte_node_logtype;
 #define NODE_LOG(level, node_name, ...)                                        \
@@ -19,4 +21,76 @@ extern int rte_node_logtype;
 #define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
 #define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store next hop, ttl and checksum.
+ */
+struct rte_node_mbuf_priv1 {
+	union {
+		/* IP4 rewrite */
+		struct {
+			uint16_t nh;
+			uint16_t ttl;
+			uint32_t cksum;
+		};
+
+		uint64_t u;
+	};
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store crypto operation.
+ */
+struct rte_node_mbuf_priv2 {
+	union {
+		/* Sym crypto */
+		struct {
+			struct rte_crypto_op op;
+		};
+	};
+} __rte_cache_aligned;
+
+#define RTE_NODE_MBUF_PRIV2_SIZE sizeof(struct rte_node_mbuf_priv2)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv1 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv1.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv1 *
+rte_node_mbuf_priv1(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv1 *)&m->udata64;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv2 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv2.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv2 *
+rte_node_mbuf_priv2(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv2 *)rte_mbuf_to_priv(m);
+}
+
 #endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/rte_node_eth_api.h b/lib/librte_node/rte_node_eth_api.h
new file mode 100644
index 000000000..39b31b45b
--- /dev/null
+++ b/lib/librte_node/rte_node_eth_api.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_ETH_API_H__
+#define __INCLUDE_RTE_NODE_ETH_API_H__
+
+/**
+ * @file rte_node_eth_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to setup ethdev_rx and ethdev_tx nodes
+ * and its queue associations.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_mempool.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Port config for ethdev_rx and ethdev_tx node.
+ */
+struct rte_node_ethdev_config {
+	uint16_t port_id;
+	/**< Port identifier */
+	uint16_t num_rx_queues;
+	/**< Number of Rx queues. */
+	uint16_t num_tx_queues;
+	/**< Number of Tx queues. */
+	struct rte_mempool **mp;
+	/**< Array of mempools associated to Rx queue. */
+	uint16_t mp_count;
+	/**< Size of mp array. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Initializes ethdev nodes.
+ *
+ * @param cfg
+ *   Array of ethdev config that identifies which port's
+ *   ethdev_rx and ethdev_tx nodes need to be created
+ *   and queue association.
+ * @param cnt
+ *   Size of cfg array.
+ * @param nb_graphs
+ *   Number of graphs that will be used.
+ *
+ * @return
+ *   0 on successful initialization, negative otherwise.
+ */
+__rte_experimental
+int rte_node_eth_config(struct rte_node_ethdev_config *cfg,
+			uint16_t cnt, uint16_t nb_graphs);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_ETH_API_H__ */
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index f87163bb9..c6c71bd02 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -1,6 +1,7 @@
 EXPERIMENTAL {
 	global:
 
+	rte_node_eth_config;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 19/29] node: add generic ipv4 lookup node
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (17 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 20/29] node: ipv4 lookup for arm64 jerinj
                       ` (10 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup node.
This node performs LPM lookup using simple RTE_LPM API on every packet
received and forwards it to a next node that is identified by lookup
result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md          |   1 +
 lib/librte_node/Makefile           |   4 +-
 lib/librte_node/ip4_lookup.c       | 128 +++++++++++++++++++++++++++++
 lib/librte_node/meson.build        |   5 +-
 lib/librte_node/rte_node_ip4_api.h |  43 ++++++++++
 5 files changed, 178 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/rte_node_ip4_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 1784674df..3908fddde 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -163,6 +163,7 @@ The public API headers are grouped by topics:
     [graph_worker]     (@ref rte_graph_worker.h)
   * graph_nodes:
     [eth_node]         (@ref rte_node_eth_api.h),
+    [ip4_node]         (@ref rte_node_ip4_api.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ea5fa77f7..ad3f2e349 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_lpm -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -21,8 +21,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 
 # install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
new file mode 100644
index 000000000..a91d42423
--- /dev/null
+++ b/lib/librte_node/ip4_lookup.c
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_lpm.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "node_private.h"
+
+#define IPV4_L3FWD_LPM_MAX_RULES 1024
+#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
+
+/* IP4 Lookup global data struct */
+struct ip4_lookup_node_main {
+	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
+};
+
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	struct rte_mbuf *mbuf;
+	rte_edge_t next_index;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	int i, rc;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+	from = objs;
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	for (i = 0; i < nb_objs; i++) {
+		uint32_t next_hop;
+		uint16_t next;
+
+		mbuf = (struct rte_mbuf *)objs[i];
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
+				sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next = (uint16_t)next_hop;
+
+		if (unlikely(next_index != next)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+static int
+ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+
+	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_lookup_node = {
+	.process = ip4_lookup_node_process,
+	.name = "ip4_lookup",
+
+	.init = ip4_lookup_node_init,
+
+	.nb_edges = RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	.next_nodes = {
+		[RTE_NODE_IP4_LOOKUP_NEXT_REWRITE] = "ip4_rewrite",
+		[RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(ip4_lookup_node);
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index af2eb2d91..702d21a24 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
-headers = files('rte_node_eth_api.h')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
+		'ethdev_ctrl.c')
+headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
new file mode 100644
index 000000000..37c12bf82
--- /dev/null
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_IP4_API_H__
+#define __INCLUDE_RTE_NODE_IP4_API_H__
+
+/**
+ * @file rte_node_ip4_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to do control path functions of ip4_* nodes
+ * like ip4_lookup, ip4_rewrite.
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * IP4 lookup next nodes.
+ */
+enum rte_node_ip4_lookup_next {
+	RTE_NODE_IP4_LOOKUP_NEXT_REWRITE,
+	/**< Rewrite node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP,
+	/**< Packet drop node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	/**< Number of next nodes of lookup node. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_IP4_API_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 20/29] node: ipv4 lookup for arm64
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (18 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 19/29] node: add generic ipv4 lookup node jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 21/29] node: ipv4 lookup for x86 jerinj
                       ` (9 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add arm64 specific IPv4 lookup process function
for ip4_lookup node. This node performs LPM lookup
on every packet received and forwards it to a next
node that is identified by lookup result.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_node/ip4_lookup.c      |   6 +
 lib/librte_node/ip4_lookup_neon.h | 238 ++++++++++++++++++++++++++++++
 2 files changed, 244 insertions(+)
 create mode 100644 lib/librte_node/ip4_lookup_neon.h

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index a91d42423..e8732e31b 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -28,6 +28,10 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+#if defined(RTE_MACHINE_CPUFLAG_NEON)
+#include "ip4_lookup_neon.h"
+#else
+
 static uint16_t
 ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 			void **objs, uint16_t nb_objs)
@@ -101,6 +105,8 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 	return nb_objs;
 }
 
+#endif
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_node/ip4_lookup_neon.h b/lib/librte_node/ip4_lookup_neon.h
new file mode 100644
index 000000000..0aafe4db2
--- /dev/null
+++ b/lib/librte_node/ip4_lookup_neon.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_IP4_LOOKUP_NEON_H__
+#define __INCLUDE_IP4_LOOKUP_NEON_H__
+
+/* ARM64 NEON */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	rte_edge_t next_index;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t result;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int32x4_t dip;
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+#define OBJS_PER_CLINE (RTE_CACHE_LINE_SIZE / sizeof(void *))
+	for (i = OBJS_PER_CLINE; i < RTE_GRAPH_BURST_SIZE; i += OBJS_PER_CLINE)
+		rte_prefetch0(&objs[i]);
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+#if RTE_GRAPH_BURST_SIZE > 64
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+#endif
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 0);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[1] = ipv4_hdr->time_to_live;
+		priv01.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 1);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[5] = ipv4_hdr->time_to_live;
+		priv01.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 2);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[1] = ipv4_hdr->time_to_live;
+		priv23.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 3);
+
+		dip = vreinterpretq_s32_u8(
+			vrev32q_u8(vreinterpretq_u8_s32(dip)));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[5] = ipv4_hdr->time_to_live;
+		priv23.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, result.u32, drop_nh);
+		priv01.u16[0] = result.u16[0];
+		priv01.u16[4] = result.u16[2];
+		priv23.u16[0] = result.u16[4];
+		priv23.u16[4] = result.u16[6];
+
+		rte_node_mbuf_priv1(mbuf0)->u = priv01.u64[0];
+		rte_node_mbuf_priv1(mbuf1)->u = priv01.u64[1];
+		rte_node_mbuf_priv1(mbuf2)->u = priv23.u64[0];
+		rte_node_mbuf_priv1(mbuf3)->u = priv23.u64[1];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec = ((next_index == result.u16[1]) &&
+				       (result.u16[1] == result.u16[3]) &&
+				       (result.u16[3] == result.u16[5]) &&
+				       (result.u16[5] == result.u16[7]));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == result.u16[1]) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[1],
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == result.u16[3]) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[3],
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == result.u16[5]) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[5],
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == result.u16[7]) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[7],
+						    from[3]);
+			}
+
+			from += 4;
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+		uint16_t next0;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next0 = (uint16_t)next_hop;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#endif /* __INCLUDE_IP4_LOOKUP_NEON_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 21/29] node: ipv4 lookup for x86
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (19 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 20/29] node: ipv4 lookup for arm64 jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 22/29] node: add ipv4 rewrite node jerinj
                       ` (8 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup
rte_node. This node performs LPM lookup using x86_64
vector supported RTE_LPM API on every packet received
and forwards it to a next node that is identified by
lookup result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ip4_lookup.c     |   2 +
 lib/librte_node/ip4_lookup_sse.h | 244 +++++++++++++++++++++++++++++++
 2 files changed, 246 insertions(+)
 create mode 100644 lib/librte_node/ip4_lookup_sse.h

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index e8732e31b..3a38f5ad8 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -30,6 +30,8 @@ struct ip4_lookup_node_main {
 
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 #include "ip4_lookup_neon.h"
+#elif defined(RTE_ARCH_X86)
+#include "ip4_lookup_sse.h"
 #else
 
 static uint16_t
diff --git a/lib/librte_node/ip4_lookup_sse.h b/lib/librte_node/ip4_lookup_sse.h
new file mode 100644
index 000000000..2efe48bf8
--- /dev/null
+++ b/lib/librte_node/ip4_lookup_sse.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_IP4_LOOKUP_SSE_H__
+#define __INCLUDE_IP4_LOOKUP_SSE_H__
+
+/* X86 SSE */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	rte_edge_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	uint32_t ip0, ip1, ip2, ip3;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t dst;
+	__m128i dip; /* SSE register */
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	if (n_left_from >= 4) {
+		for (i = 0; i < 4; i++)
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+	}
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip0 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip1 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip2 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip3 = ipv4_hdr->dst_addr;
+
+		/* Prepare for lookup x4 */
+		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
+
+		/* Byte swap 4 IPV4 addresses. */
+		const __m128i bswap_mask = _mm_set_epi8(
+			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
+		dip = _mm_shuffle_epi8(dip, bswap_mask);
+
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr->time_to_live;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
+
+		/* Extract next node id and NH */
+		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] & 0xFFFF;
+		next0 = (dst.u32[0] >> 16);
+
+		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] & 0xFFFF;
+		next1 = (dst.u32[1] >> 16);
+
+		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] & 0xFFFF;
+		next2 = (dst.u32[2] >> 16);
+
+		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] & 0xFFFF;
+		next3 = (dst.u32[3] >> 16);
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			(next_index ^ next0) | (next_index ^ next1) |
+			(next_index ^ next2) | (next_index ^ next3);
+
+		if (unlikely(fix_spec)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = next_hop & 0xFFFF;
+		next0 = (next_hop >> 16);
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	/* Copy things successfully speculated till now */
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#endif /* __INCLUDE_IP4_LOOKUP_SSE_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 22/29] node: add ipv4 rewrite node
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (20 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 21/29] node: ipv4 lookup for x86 jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
                       ` (7 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Kiran Kumar K <kirankumark@marvell.com>

Add ip4 rewrite process function for ip4_rewrite
rte_node. On every packet received by this node,
header is overwritten with new data before forwarding
it to next node. Header data to overwrite with is
identified by next hop id passed in mbuf priv data
by previous node.

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_node/Makefile           |   1 +
 lib/librte_node/ip4_rewrite.c      | 270 +++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h |  55 ++++++
 lib/librte_node/meson.build        |   2 +-
 4 files changed, 327 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ad3f2e349..ebf473c66 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -22,6 +22,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
new file mode 100644
index 000000000..ef49ccea0
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite.c
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_vect.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "ip4_rewrite_priv.h"
+#include "node_private.h"
+
+static struct ip4_rewrite_node_main *ip4_rewrite_nm;
+
+static uint16_t
+ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
+			 void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct ip4_rewrite_nh_header *nh = ip4_rewrite_nm->nh;
+	uint16_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ip0, *ip1, *ip2, *ip3;
+	uint16_t n_left_from, held = 0, last_spec = 0;
+	void *d0, *d1, *d2, *d3;
+	void **to_next, **from;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int i;
+
+	/* Speculative next as last next */
+	next_index = *(uint16_t *)node->ctx;
+	rte_prefetch0(nh);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(pkts[i]);
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	/* Update Ethernet header of pkts */
+	while (n_left_from >= 4) {
+		if (likely(n_left_from > 7)) {
+			/* Prefetch only next-mbuf struct and priv area.
+			 * Data need not be prefetched as we only write.
+			 */
+			rte_prefetch0(pkts[4]);
+			rte_prefetch0(pkts[5]);
+			rte_prefetch0(pkts[6]);
+			rte_prefetch0(pkts[7]);
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+		priv01.u64[0] = rte_node_mbuf_priv1(mbuf0)->u;
+		priv01.u64[1] = rte_node_mbuf_priv1(mbuf1)->u;
+		priv23.u64[0] = rte_node_mbuf_priv1(mbuf2)->u;
+		priv23.u64[1] = rte_node_mbuf_priv1(mbuf3)->u;
+
+		/* Increment checksum by one. */
+		priv01.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv01.u32[3] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[3] += rte_cpu_to_be_16(0x0100);
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf0 */
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[priv01.u16[0]].rewrite_data,
+			   nh[priv01.u16[0]].rewrite_len);
+
+		next0 = nh[priv01.u16[0]].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		ip0->time_to_live = priv01.u16[1] - 1;
+		ip0->hdr_checksum = priv01.u16[2] + priv01.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf1 */
+		d1 = rte_pktmbuf_mtod(mbuf1, void *);
+		rte_memcpy(d1, nh[priv01.u16[4]].rewrite_data,
+			   nh[priv01.u16[4]].rewrite_len);
+
+		next1 = nh[priv01.u16[4]].tx_node;
+		ip1 = (struct rte_ipv4_hdr *)((uint8_t *)d1 +
+					      sizeof(struct rte_ether_hdr));
+		ip1->time_to_live = priv01.u16[5] - 1;
+		ip1->hdr_checksum = priv01.u16[6] + priv01.u16[7];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf2 */
+		d2 = rte_pktmbuf_mtod(mbuf2, void *);
+		rte_memcpy(d2, nh[priv23.u16[0]].rewrite_data,
+			   nh[priv23.u16[0]].rewrite_len);
+		next2 = nh[priv23.u16[0]].tx_node;
+		ip2 = (struct rte_ipv4_hdr *)((uint8_t *)d2 +
+					      sizeof(struct rte_ether_hdr));
+		ip2->time_to_live = priv23.u16[1] - 1;
+		ip2->hdr_checksum = priv23.u16[2] + priv23.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf3 */
+		d3 = rte_pktmbuf_mtod(mbuf3, void *);
+		rte_memcpy(d3, nh[priv23.u16[4]].rewrite_data,
+			   nh[priv23.u16[4]].rewrite_len);
+
+		next3 = nh[priv23.u16[4]].tx_node;
+		ip3 = (struct rte_ipv4_hdr *)((uint8_t *)d3 +
+					      sizeof(struct rte_ether_hdr));
+		ip3->time_to_live = priv23.u16[5] - 1;
+		ip3->hdr_checksum = priv23.u16[6] + priv23.u16[7];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			((next_index == next0) && (next0 == next1) &&
+			 (next1 == next2) && (next2 == next3));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+			/* Change speculation if last two are same */
+			if ((next_index != next3) && (next2 == next3)) {
+				/* Put the current speculated node */
+				rte_node_next_stream_put(graph, node,
+							 next_index, held);
+				held = 0;
+
+				/* Get next speculated stream */
+				next_index = next3;
+				to_next = rte_node_next_stream_get(
+					graph, node, next_index, nb_objs);
+			}
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint16_t chksum;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_data,
+			   nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_len);
+
+		next0 = nh[rte_node_mbuf_priv1(mbuf0)->nh].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		chksum = rte_node_mbuf_priv1(mbuf0)->cksum +
+			 rte_cpu_to_be_16(0x0100);
+		chksum += chksum >= 0xffff;
+		ip0->hdr_checksum = chksum;
+		ip0->time_to_live = rte_node_mbuf_priv1(mbuf0)->ttl - 1;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+	/* Save the last next used */
+	*(uint16_t *)node->ctx = next_index;
+
+	return nb_objs;
+}
+
+static int
+ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_rewrite_node = {
+	.process = ip4_rewrite_node_process,
+	.name = "ip4_rewrite",
+	/* Default edge i.e '0' is pkt drop */
+	.nb_edges = 1,
+	.next_nodes = {
+		[0] = "pkt_drop",
+	},
+	.init = ip4_rewrite_node_init,
+};
+
+RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
new file mode 100644
index 000000000..420996a03
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_IP4_REWRITE_PRIV_H__
+#define __INCLUDE_IP4_REWRITE_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+#define RTE_GRAPH_IP4_REWRITE_MAX_NH 64
+#define RTE_GRAPH_IP4_REWRITE_MAX_LEN 56
+
+/**
+ * @internal
+ *
+ * Ipv4 rewrite next hop header data structure. Used to store port specific
+ * rewrite data.
+ */
+struct ip4_rewrite_nh_header {
+	uint16_t rewrite_len; /**< Header rewrite length. */
+	uint16_t tx_node;     /**< Tx node next index identifier. */
+	uint16_t enabled;     /**< NH enable flag */
+	uint16_t rsvd;
+	union {
+		struct {
+			struct rte_ether_addr dst;
+			/**< Destination mac address. */
+			struct rte_ether_addr src;
+			/**< Source mac address. */
+		};
+		uint8_t rewrite_data[RTE_GRAPH_IP4_REWRITE_MAX_LEN];
+		/**< Generic rewrite data */
+	};
+};
+
+/**
+ * @internal
+ *
+ * Ipv4 node main data structure.
+ */
+struct ip4_rewrite_node_main {
+	struct ip4_rewrite_nh_header nh[RTE_GRAPH_IP4_REWRITE_MAX_NH];
+	/**< Array of next hop header data */
+	uint16_t next_index[RTE_MAX_ETHPORTS];
+	/**< Next index of each configured port. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_IP4_REWRITE_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 702d21a24..ed78791dd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 23/29] node: add ipv4 rewrite and lookup ctrl API
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (21 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 22/29] node: add ipv4 rewrite node jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 24/29] node: add packet drop node jerinj
                       ` (6 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ip4_rewrite and ip4_lookup ctrl API. ip4_lookup ctrl
API is used to add route entries for LPM lookup with
result data containing next hop id and next proto.
ip4_rewrite ctrl API is used to add rewrite data for
every next hop.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ethdev_ctrl.c        | 18 ++++++-
 lib/librte_node/ip4_lookup.c         | 80 ++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite.c        | 56 +++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h   | 22 ++++++++
 lib/librte_node/rte_node_ip4_api.h   | 44 +++++++++++++++
 lib/librte_node/rte_node_version.map |  2 +
 6 files changed, 221 insertions(+), 1 deletion(-)

diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
index b2ac5e2c4..845d92987 100644
--- a/lib/librte_node/ethdev_ctrl.c
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -11,6 +11,7 @@
 
 #include "ethdev_rx_priv.h"
 #include "ethdev_tx_priv.h"
+#include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
 static struct ethdev_ctrl {
@@ -21,14 +22,17 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 		    uint16_t nb_graphs)
 {
+	struct rte_node_register *ip4_rewrite_node;
 	struct ethdev_tx_node_main *tx_node_data;
 	uint16_t tx_q_used, rx_q_used, port_id;
 	struct rte_node_register *tx_node;
 	char name[RTE_NODE_NAMESIZE];
+	const char *next_nodes = name;
 	struct rte_mempool *mp;
+	int i, j, rc;
 	uint32_t id;
-	int i, j;
 
+	ip4_rewrite_node = ip4_rewrite_node_get();
 	tx_node_data = ethdev_tx_node_data_get();
 	tx_node = ethdev_tx_node_get();
 	for (i = 0; i < nb_confs; i++) {
@@ -93,6 +97,18 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 
 		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
 			 name, id);
+
+		/* Prepare the actual name of the cloned node */
+		snprintf(name, sizeof(name), "ethdev_tx-%u", port_id);
+
+		/* Add this tx port node as next to ip4_rewrite_node */
+		rte_node_edge_update(ip4_rewrite_node->id, RTE_EDGE_ID_INVALID,
+				     &next_nodes, 1);
+		/* Assuming edge id is the last one alloc'ed */
+		rc = ip4_rewrite_set_next(
+			port_id, rte_node_edge_count(ip4_rewrite_node->id) - 1);
+		if (rc < 0)
+			return rc;
 	}
 
 	ctrl.nb_graphs = nb_graphs;
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index 3a38f5ad8..d10d17879 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -28,6 +28,8 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+static struct ip4_lookup_node_main ip4_lookup_nm;
+
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 #include "ip4_lookup_neon.h"
 #elif defined(RTE_ARCH_X86)
@@ -109,12 +111,90 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 
 #endif
 
+int
+rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+		       enum rte_node_ip4_lookup_next next_node)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	struct in_addr in;
+	uint8_t socket;
+	uint32_t val;
+	int ret;
+
+	in.s_addr = htonl(ip);
+	inet_ntop(AF_INET, &in, abuf, sizeof(abuf));
+	/* Embedded next node id in next hop */
+	val = (next_node << 16) | next_hop;
+	node_dbg("ip4_lookup", "LPM: Adding route %s / %d nh (0x%x)", abuf,
+		 depth, val);
+
+	for (socket = 0; socket < RTE_MAX_NUMA_NODES; socket++) {
+		if (!ip4_lookup_nm.lpm_tbl[socket])
+			continue;
+
+		ret = rte_lpm_add(ip4_lookup_nm.lpm_tbl[socket], ip, depth,
+				  val);
+
+		if (ret < 0) {
+			node_err("ip4_lookup",
+				 "Unable to add entry %s / %d nh (%x) to LPM table on sock %d, rc=%d\n",
+				 abuf, depth, val, socket, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int
+setup_lpm(struct ip4_lookup_node_main *nm, int socket)
+{
+	struct rte_lpm_config config_ipv4;
+	char s[RTE_LPM_NAMESIZE];
+
+	/* One LPM table per socket */
+	if (nm->lpm_tbl[socket])
+		return 0;
+
+	/* create the LPM table */
+	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
+	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
+	config_ipv4.flags = 0;
+	snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socket);
+	nm->lpm_tbl[socket] = rte_lpm_create(s, socket, &config_ipv4);
+	if (nm->lpm_tbl[socket] == NULL)
+		return -rte_errno;
+
+	return 0;
+}
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
+	struct rte_lpm **lpm_p = (struct rte_lpm **)&node->ctx;
+	uint16_t socket, lcore_id;
+	static uint8_t init_once;
+	int rc;
+
 	RTE_SET_USED(graph);
 	RTE_SET_USED(node);
 
+	if (!init_once) {
+		/* Setup LPM tables for all sockets */
+		RTE_LCORE_FOREACH(lcore_id)
+		{
+			socket = rte_lcore_to_socket_id(lcore_id);
+			rc = setup_lpm(&ip4_lookup_nm, socket);
+			if (rc) {
+				node_err("ip4_lookup",
+					 "Failed to setup lpm tbl for sock %u, rc=%d",
+					 socket, rc);
+				return rc;
+			}
+		}
+		init_once = 1;
+	}
+	*lpm_p = ip4_lookup_nm.lpm_tbl[graph->socket];
 	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
 
 	return 0;
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
index ef49ccea0..5663f1eb1 100644
--- a/lib/librte_node/ip4_rewrite.c
+++ b/lib/librte_node/ip4_rewrite.c
@@ -256,6 +256,56 @@ ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 	return 0;
 }
 
+int
+ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index)
+{
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+	ip4_rewrite_nm->next_index[port_id] = next_index;
+
+	return 0;
+}
+
+int
+rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			 uint8_t rewrite_len, uint16_t dst_port)
+{
+	struct ip4_rewrite_nh_header *nh;
+
+	if (next_hop >= RTE_GRAPH_IP4_REWRITE_MAX_NH)
+		return -EINVAL;
+
+	if (rewrite_len > RTE_GRAPH_IP4_REWRITE_MAX_LEN)
+		return -EINVAL;
+
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+
+	/* Check if dst port doesn't exist as edge */
+	if (!ip4_rewrite_nm->next_index[dst_port])
+		return -EINVAL;
+
+	/* Update next hop */
+	nh = &ip4_rewrite_nm->nh[next_hop];
+
+	memcpy(nh->rewrite_data, rewrite_data, rewrite_len);
+	nh->tx_node = ip4_rewrite_nm->next_index[dst_port];
+	nh->rewrite_len = rewrite_len;
+	nh->enabled = true;
+
+	return 0;
+}
+
 static struct rte_node_register ip4_rewrite_node = {
 	.process = ip4_rewrite_node_process,
 	.name = "ip4_rewrite",
@@ -267,4 +317,10 @@ static struct rte_node_register ip4_rewrite_node = {
 	.init = ip4_rewrite_node_init,
 };
 
+struct rte_node_register *
+ip4_rewrite_node_get(void)
+{
+	return &ip4_rewrite_node;
+}
+
 RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
index 420996a03..80f0abdc9 100644
--- a/lib/librte_node/ip4_rewrite_priv.h
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -48,6 +48,28 @@ struct ip4_rewrite_node_main {
 	/**< Next index of each configured port. */
 };
 
+/**
+ * @internal
+ *
+ * Get the ipv4 rewrite node.
+ *
+ * @retrun
+ *   Pointer to the ipv4 rewrite node.
+ */
+struct rte_node_register *ip4_rewrite_node_get(void);
+
+/**
+ * @internal
+ *
+ * Set the Edge index of a given port_id.
+ *
+ * @param port_id
+ *   Ethernet port identifier.
+ * @param next_index
+ *   Edge index of the Given Tx node.
+ */
+int ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
index 37c12bf82..394cac097 100644
--- a/lib/librte_node/rte_node_ip4_api.h
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -36,6 +36,50 @@ enum rte_node_ip4_lookup_next {
 	/**< Number of next nodes of lookup node. */
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add ipv4 route to lookup table.
+ *
+ * @param ip
+ *   IP address of route to be added.
+ * @param depth
+ *   Depth of the rule to be added.
+ * @param next_hop
+ *   Next hop id of the rule result to be added.
+ * @param next_node
+ *   Next node to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+			   enum rte_node_ip4_lookup_next next_node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add a next hop's rewrite data.
+ *
+ * @param next_hop
+ *   Next hop id to add rewrite data to.
+ * @param rewrite_data
+ *   Rewrite data.
+ * @param rewrite_len
+ *   Length of rewrite data.
+ * @param dst_port
+ *   Destination port to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			     uint8_t rewrite_len, uint16_t dst_port);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index c6c71bd02..a799b0d38 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -2,6 +2,8 @@ EXPERIMENTAL {
 	global:
 
 	rte_node_eth_config;
+	rte_node_ip4_route_add;
+	rte_node_ip4_rewrite_add;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v3 24/29] node: add packet drop node
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (22 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
                       ` (5 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add packet drop node process function for pkt_drop
rte_node. This node simply free's every object received as
an rte_mbuf to its rte_pktmbuf pool.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile    |  1 +
 lib/librte_node/meson.build |  2 +-
 lib/librte_node/pkt_drop.c  | 26 ++++++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/pkt_drop.c

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ebf473c66..322b651c8 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -23,6 +23,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += pkt_drop.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index ed78791dd..8aa93654b 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ip4_rewrite.c', 'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'pkt_drop.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/librte_node/pkt_drop.c b/lib/librte_node/pkt_drop.c
new file mode 100644
index 000000000..c35001323
--- /dev/null
+++ b/lib/librte_node/pkt_drop.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_graph.h>
+#include <rte_mbuf.h>
+
+static uint16_t
+pkt_drop_process(struct rte_graph *graph, struct rte_node *node, void **objs,
+		 uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(graph);
+
+	rte_pktmbuf_free_bulk((struct rte_mbuf **)objs, nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register pkt_drop_node = {
+	.process = pkt_drop_process,
+	.name = "pkt_drop",
+};
+
+RTE_NODE_REGISTER(pkt_drop_node);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 25/29] l3fwd-graph: add graph based l3fwd skeleton
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (23 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 24/29] node: add packet drop node jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 26/29] l3fwd-graph: add ethdev configuration changes jerinj
                       ` (4 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Thomas Monjalon, Marko Kovacevic, Ori Kam, Bruce Richardson,
	Radu Nicolau, Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori,
	Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph based l3fwd application skeleton with cmdline
parsing support inline with normal l3fwd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                      |   3 +
 examples/Makefile                |   3 +
 examples/l3fwd-graph/Makefile    |  58 ++++
 examples/l3fwd-graph/main.c      | 525 +++++++++++++++++++++++++++++++
 examples/l3fwd-graph/meson.build |  13 +
 examples/meson.build             |   6 +-
 6 files changed, 606 insertions(+), 2 deletions(-)
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index c1acaedab..1d2cf6caa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1581,6 +1581,9 @@ F: doc/guides/sample_app_ug/l2_forward_event.rst
 F: examples/l3fwd/
 F: doc/guides/sample_app_ug/l3_forward.rst
 
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+F: examples/l3fwd-graph/
+
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
 
diff --git a/examples/Makefile b/examples/Makefile
index feff79784..b7e99a2f7 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -51,6 +51,9 @@ DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl
 ifeq ($(CONFIG_RTE_LIBRTE_LPM)$(CONFIG_RTE_LIBRTE_HASH),yy)
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH),y)
+DIRS-y += l3fwd-graph
+endif
 DIRS-y += link_status_interrupt
 DIRS-y += multi_process
 DIRS-y += ntb
diff --git a/examples/l3fwd-graph/Makefile b/examples/l3fwd-graph/Makefile
new file mode 100644
index 000000000..f3cf275ec
--- /dev/null
+++ b/examples/l3fwd-graph/Makefile
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# binary name
+APP = l3fwd-graph
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+# Build using pkg-config variables if possible
+ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)
+
+all: shared
+.PHONY: shared static
+shared: build/$(APP)-shared
+	ln -sf $(APP)-shared build/$(APP)
+static: build/$(APP)-static
+	ln -sf $(APP)-static build/$(APP)
+
+PKGCONF=pkg-config --define-prefix
+
+PC_FILE := $(shell $(PKGCONF) --path libdpdk)
+CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) -DALLOW_EXPERIMENTAL_API
+LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
+LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)
+
+build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
+
+build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
+
+build:
+	@mkdir -p $@
+
+.PHONY: clean
+clean:
+	rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
+	test -d build && rmdir -p build || true
+
+else # Build using legacy build system
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, detect a build directory, by looking for a path with a .config
+RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -O3 $(USER_FLAGS)
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+endif
diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
new file mode 100644
index 000000000..e0c6f42fa
--- /dev/null
+++ b/examples/l3fwd-graph/main.c
@@ -0,0 +1,525 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_branch_prediction.h>
+#include <rte_common.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_per_lcore.h>
+#include <rte_string_fns.h>
+#include <rte_vect.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
+
+/* Log type */
+#define RTE_LOGTYPE_L3FWD_GRAPH RTE_LOGTYPE_USER1
+
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 1024
+#define RTE_TEST_TX_DESC_DEFAULT 1024
+
+#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
+#define MAX_RX_QUEUE_PER_PORT 128
+
+#define MAX_RX_QUEUE_PER_LCORE 16
+
+#define MAX_LCORE_PARAMS 1024
+
+#define NB_SOCKETS 8
+
+/**< Ports set in promiscuous mode off by default. */
+static int promiscuous_on;
+
+static int numa_on = 1;	  /**< NUMA is enabled by default. */
+static int per_port_pool; /**< Use separate buffer pools per port; disabled */
+			  /**< by default */
+
+static volatile bool force_quit;
+
+/* Ethernet addresses of ports */
+static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+xmm_t val_eth[RTE_MAX_ETHPORTS];
+
+/* Mask of enabled ports */
+static uint32_t enabled_port_mask;
+
+struct lcore_rx_queue {
+	uint16_t port_id;
+	uint8_t queue_id;
+};
+
+/* Lcore conf */
+struct lcore_conf {
+	uint16_t n_rx_queue;
+	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+} __rte_cache_aligned;
+
+static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+
+struct lcore_params {
+	uint16_t port_id;
+	uint8_t queue_id;
+	uint8_t lcore_id;
+} __rte_cache_aligned;
+
+static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
+static struct lcore_params lcore_params_array_default[] = {
+	{0, 0, 2}, {0, 1, 2}, {0, 2, 2}, {1, 0, 2}, {1, 1, 2},
+	{1, 2, 2}, {2, 0, 2}, {3, 0, 3}, {3, 1, 3},
+};
+
+static struct lcore_params *lcore_params = lcore_params_array_default;
+static uint16_t nb_lcore_params = RTE_DIM(lcore_params_array_default);
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_RSS,
+		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+	},
+	.rx_adv_conf = {
+		.rss_conf = {
+				.rss_key = NULL,
+				.rss_hf = ETH_RSS_IP,
+		},
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static int
+check_lcore_params(void)
+{
+	uint8_t queue, lcore;
+	int socketid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		queue = lcore_params[i].queue_id;
+		if (queue >= MAX_RX_QUEUE_PER_PORT) {
+			printf("Invalid queue number: %hhu\n", queue);
+			return -1;
+		}
+		lcore = lcore_params[i].lcore_id;
+		if (!rte_lcore_is_enabled(lcore)) {
+			printf("Error: lcore %hhu is not enabled in lcore mask\n",
+			       lcore);
+			return -1;
+		}
+
+		if (lcore == rte_get_master_lcore()) {
+			printf("Error: lcore %u is master lcore\n", lcore);
+			return -1;
+		}
+		socketid = rte_lcore_to_socket_id(lcore);
+		if ((socketid != 0) && (numa_on == 0)) {
+			printf("Warning: lcore %hhu is on socket %d with numa off\n",
+			       lcore, socketid);
+		}
+	}
+
+	return 0;
+}
+
+static int
+check_port_config(void)
+{
+	uint16_t portid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		portid = lcore_params[i].port_id;
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("Port %u is not enabled in port mask\n", portid);
+			return -1;
+		}
+		if (!rte_eth_dev_is_valid_port(portid)) {
+			printf("Port %u is not present on the board\n", portid);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+init_lcore_rx_queues(void)
+{
+	uint16_t i, nb_rx_queue;
+	uint8_t lcore;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		lcore = lcore_params[i].lcore_id;
+		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
+			printf("Error: too many queues (%u) for lcore: %u\n",
+			       (unsigned int)nb_rx_queue + 1,
+			       (unsigned int)lcore);
+			return -1;
+		}
+
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+			lcore_params[i].port_id;
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+			lcore_params[i].queue_id;
+		lcore_conf[lcore].n_rx_queue++;
+	}
+
+	return 0;
+}
+
+/* Display usage */
+static void
+print_usage(const char *prgname)
+{
+	fprintf(stderr,
+		"%s [EAL options] --"
+		" -p PORTMASK"
+		" [-P]"
+		" --config (port,queue,lcore)[,(port,queue,lcore)]"
+		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
+		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
+		" [--no-numa]"
+		" [--per-port-pool]\n\n"
+
+		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
+		"  -P : Enable promiscuous mode\n"
+		"  --config (port,queue,lcore): Rx queue configuration\n"
+		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for "
+		"port X\n"
+		"  --enable-jumbo: Enable jumbo frames\n"
+		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"                 maximum packet length in decimal (64-9600)\n"
+		"  --no-numa: Disable numa awareness\n"
+		"  --per-port-pool: Use separate buffer pool per port\n\n",
+		prgname);
+}
+
+static int
+parse_max_pkt_len(const char *pktlen)
+{
+	unsigned long len;
+	char *end = NULL;
+
+	/* Parse decimal string */
+	len = strtoul(pktlen, &end, 10);
+	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (len == 0)
+		return -1;
+
+	return len;
+}
+
+static int
+parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_config(const char *q_arg)
+{
+	enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD };
+	unsigned long int_fld[_NUM_FLD];
+	const char *p, *p0 = q_arg;
+	char *str_fld[_NUM_FLD];
+	uint32_t size;
+	char s[256];
+	char *end;
+	int i;
+
+	nb_lcore_params = 0;
+
+	while ((p = strchr(p0, '(')) != NULL) {
+		++p;
+		p0 = strchr(p, ')');
+		if (p0 == NULL)
+			return -1;
+
+		size = p0 - p;
+		if (size >= sizeof(s))
+			return -1;
+
+		snprintf(s, sizeof(s), "%.*s", size, p);
+		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
+		    _NUM_FLD)
+			return -1;
+		for (i = 0; i < _NUM_FLD; i++) {
+			errno = 0;
+			int_fld[i] = strtoul(str_fld[i], &end, 0);
+			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
+				return -1;
+		}
+		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
+			printf("Exceeded max number of lcore params: %hu\n",
+			       nb_lcore_params);
+			return -1;
+		}
+		lcore_params_array[nb_lcore_params].port_id =
+			(uint8_t)int_fld[FLD_PORT];
+		lcore_params_array[nb_lcore_params].queue_id =
+			(uint8_t)int_fld[FLD_QUEUE];
+		lcore_params_array[nb_lcore_params].lcore_id =
+			(uint8_t)int_fld[FLD_LCORE];
+		++nb_lcore_params;
+	}
+	lcore_params = lcore_params_array;
+
+	return 0;
+}
+
+static void
+parse_eth_dest(const char *optarg)
+{
+	uint8_t c, *dest, peer_addr[6];
+	uint16_t portid;
+	char *port_end;
+
+	errno = 0;
+	portid = strtoul(optarg, &port_end, 10);
+	if (errno != 0 || port_end == optarg || *port_end++ != ',')
+		rte_exit(EXIT_FAILURE, "Invalid eth-dest: %s", optarg);
+	if (portid >= RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE,
+			 "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", portid,
+			 RTE_MAX_ETHPORTS);
+
+	if (cmdline_parse_etheraddr(NULL, port_end, &peer_addr,
+				    sizeof(peer_addr)) < 0)
+		rte_exit(EXIT_FAILURE, "Invalid ethernet address: %s\n",
+			 port_end);
+	dest = (uint8_t *)&dest_eth_addr[portid];
+	for (c = 0; c < 6; c++)
+		dest[c] = peer_addr[c];
+	*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+}
+
+#define MAX_JUMBO_PKT_LEN  9600
+#define MEMPOOL_CACHE_SIZE 256
+
+static const char short_options[] = "p:" /* portmask */
+				    "P"	 /* promiscuous */
+	;
+
+#define CMD_LINE_OPT_CONFIG	   "config"
+#define CMD_LINE_OPT_ETH_DEST	   "eth-dest"
+#define CMD_LINE_OPT_NO_NUMA	   "no-numa"
+#define CMD_LINE_OPT_ENABLE_JUMBO  "enable-jumbo"
+#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool"
+enum {
+	/* Long options mapped to a short option */
+
+	/* First long only option value must be >= 256, so that we won't
+	 * conflict with short options
+	 */
+	CMD_LINE_OPT_MIN_NUM = 256,
+	CMD_LINE_OPT_CONFIG_NUM,
+	CMD_LINE_OPT_ETH_DEST_NUM,
+	CMD_LINE_OPT_NO_NUMA_NUM,
+	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
+	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
+};
+
+static const struct option lgopts[] = {
+	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL},
+	{NULL, 0, 0, 0},
+};
+
+/*
+ * This expression is used to calculate the number of mbufs needed
+ * depending on user input, taking  into account memory for rx and
+ * tx hardware rings, cache per lcore and mtable per port per lcore.
+ * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum
+ * value of 8192
+ */
+#define NB_MBUF(nports)                                                        \
+	RTE_MAX((nports * nb_rx_queue * nb_rxd +                               \
+		 nports * nb_lcores * RTE_GRAPH_BURST_SIZE +                   \
+		 nports * n_tx_queue * nb_txd +                                \
+		 nb_lcores * MEMPOOL_CACHE_SIZE), 8192u)
+
+/* Parse the argument given in the command line of the application */
+static int
+parse_args(int argc, char **argv)
+{
+	char *prgname = argv[0];
+	int option_index;
+	char **argvopt;
+	int opt, ret;
+
+	argvopt = argv;
+
+	/* Error or normal output strings. */
+	while ((opt = getopt_long(argc, argvopt, short_options, lgopts,
+				  &option_index)) != EOF) {
+
+		switch (opt) {
+		/* Portmask */
+		case 'p':
+			enabled_port_mask = parse_portmask(optarg);
+			if (enabled_port_mask == 0) {
+				fprintf(stderr, "Invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case 'P':
+			promiscuous_on = 1;
+			break;
+
+		/* Long options */
+		case CMD_LINE_OPT_CONFIG_NUM:
+			ret = parse_config(optarg);
+			if (ret) {
+				fprintf(stderr, "Invalid config\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case CMD_LINE_OPT_ETH_DEST_NUM:
+			parse_eth_dest(optarg);
+			break;
+
+		case CMD_LINE_OPT_NO_NUMA_NUM:
+			numa_on = 0;
+			break;
+
+		case CMD_LINE_OPT_ENABLE_JUMBO_NUM: {
+			const struct option lenopts = {"max-pkt-len",
+						       required_argument, 0, 0};
+
+			port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME;
+			port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS;
+
+			/*
+			 * if no max-pkt-len set, use the default
+			 * value RTE_ETHER_MAX_LEN.
+			 */
+			if (getopt_long(argc, argvopt, "", &lenopts,
+					&option_index) == 0) {
+				ret = parse_max_pkt_len(optarg);
+				if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) {
+					fprintf(stderr, "Invalid maximum "
+							"packet length\n");
+					print_usage(prgname);
+					return -1;
+				}
+				port_conf.rxmode.max_rx_pkt_len = ret;
+			}
+			break;
+		}
+
+		case CMD_LINE_OPT_PARSE_PER_PORT_POOL:
+			printf("Per port buffer pool is enabled\n");
+			per_port_pool = 1;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind - 1] = prgname;
+	ret = optind - 1;
+	optind = 1; /* Reset getopt lib */
+
+	return ret;
+}
+
+static void
+signal_handler(int signum)
+{
+	if (signum == SIGINT || signum == SIGTERM) {
+		printf("\n\nSignal %d received, preparing to exit...\n",
+		       signum);
+		force_quit = true;
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	uint16_t portid;
+	int ret;
+
+	/* Init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+	argc -= ret;
+	argv += ret;
+
+	force_quit = false;
+	signal(SIGINT, signal_handler);
+	signal(SIGTERM, signal_handler);
+
+	/* Pre-init dst MACs for all ports to 02:00:00:00:00:xx */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		dest_eth_addr[portid] =
+			RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40);
+		*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+	}
+
+	/* Parse application arguments (after the EAL ones) */
+	ret = parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L3FWD_GRAPH parameters\n");
+
+	if (check_lcore_params() < 0)
+		rte_exit(EXIT_FAILURE, "check_lcore_params() failed\n");
+
+	ret = init_lcore_rx_queues();
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues() failed\n");
+
+	if (check_port_config() < 0)
+		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
+
+	printf("Bye...\n");
+
+	return ret;
+}
diff --git a/examples/l3fwd-graph/meson.build b/examples/l3fwd-graph/meson.build
new file mode 100644
index 000000000..a816bd890
--- /dev/null
+++ b/examples/l3fwd-graph/meson.build
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+deps += ['graph', 'eal', 'lpm', 'ethdev', 'node' ]
+sources = files(
+	'main.c'
+)
+allow_experimental_apis = true
diff --git a/examples/meson.build b/examples/meson.build
index 1f2b6f516..3b540012f 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -2,8 +2,10 @@
 # Copyright(c) 2017-2019 Intel Corporation
 
 driver_libs = []
+node_libs = []
 if get_option('default_library') == 'static'
 	driver_libs = dpdk_drivers
+	node_libs = dpdk_graph_nodes
 endif
 
 execinfo = cc.find_library('execinfo', required: false)
@@ -23,7 +25,7 @@ all_examples = [
 	'l2fwd', 'l2fwd-cat', 'l2fwd-event',
 	'l2fwd-crypto', 'l2fwd-jobstats',
 	'l2fwd-keepalive', 'l3fwd',
-	'l3fwd-acl', 'l3fwd-power',
+	'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph',
 	'link_status_interrupt',
 	'multi_process/client_server_mp/mp_client',
 	'multi_process/client_server_mp/mp_server',
@@ -99,7 +101,7 @@ foreach example: examples
 		endif
 		executable('dpdk-' + name, sources,
 			include_directories: includes,
-			link_whole: driver_libs,
+			link_whole: driver_libs + node_libs,
 			link_args: dpdk_extra_ldflags,
 			c_args: cflags,
 			dependencies: dep_objs)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 26/29] l3fwd-graph: add ethdev configuration changes
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (24 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 27/29] l3fwd-graph: add graph config and main loop jerinj
                       ` (3 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add changes to ethdev port and queue configuration based
on command line parameters for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 350 +++++++++++++++++++++++++++++++++++-
 1 file changed, 349 insertions(+), 1 deletion(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index e0c6f42fa..47d2f2ecb 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -20,8 +20,10 @@
 
 #include <rte_branch_prediction.h>
 #include <rte_common.h>
+#include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
 #include <rte_per_lcore.h>
@@ -49,6 +51,10 @@
 
 #define NB_SOCKETS 8
 
+/* Static global variables used within this file. */
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
@@ -60,6 +66,7 @@ static volatile bool force_quit;
 
 /* Ethernet addresses of ports */
 static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+static struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
 xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 /* Mask of enabled ports */
@@ -110,6 +117,8 @@ static struct rte_eth_conf port_conf = {
 	},
 };
 
+static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
+
 static int
 check_lcore_params(void)
 {
@@ -165,6 +174,27 @@ check_port_config(void)
 	return 0;
 }
 
+static uint8_t
+get_port_n_rx_queues(const uint16_t port)
+{
+	int queue = -1;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		if (lcore_params[i].port_id == port) {
+			if (lcore_params[i].queue_id == queue + 1)
+				queue = lcore_params[i].queue_id;
+			else
+				rte_exit(EXIT_FAILURE,
+					 "Queue ids of the port %d must be"
+					 " in sequence and must start with 0\n",
+					 lcore_params[i].port_id);
+		}
+	}
+
+	return (uint8_t)(++queue);
+}
+
 static int
 init_lcore_rx_queues(void)
 {
@@ -470,6 +500,120 @@ parse_args(int argc, char **argv)
 	return ret;
 }
 
+static void
+print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
+{
+	char buf[RTE_ETHER_ADDR_FMT_SIZE];
+	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
+	printf("%s%s", name, buf);
+}
+
+static int
+init_mem(uint16_t portid, uint32_t nb_mbuf)
+{
+	uint32_t lcore_id;
+	int socketid;
+	char s[64];
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		if (numa_on)
+			socketid = rte_lcore_to_socket_id(lcore_id);
+		else
+			socketid = 0;
+
+		if (socketid >= NB_SOCKETS) {
+			rte_exit(EXIT_FAILURE,
+				 "Socket %d of lcore %u is out of range %d\n",
+				 socketid, lcore_id, NB_SOCKETS);
+		}
+
+		if (pktmbuf_pool[portid][socketid] == NULL) {
+			snprintf(s, sizeof(s), "mbuf_pool_%d:%d", portid,
+				 socketid);
+			/* Create a pool with priv size of a cacheline */
+			pktmbuf_pool[portid][socketid] =
+				rte_pktmbuf_pool_create(
+					s, nb_mbuf, MEMPOOL_CACHE_SIZE,
+					RTE_CACHE_LINE_SIZE,
+					RTE_MBUF_DEFAULT_BUF_SIZE, socketid);
+			if (pktmbuf_pool[portid][socketid] == NULL)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init mbuf pool on socket %d\n",
+					 socketid);
+			else
+				printf("Allocated mbuf pool on socket %d\n",
+				       socketid);
+		}
+	}
+
+	return 0;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90  /* 9s (90 * 100ms) in total */
+	uint8_t count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+	uint16_t portid;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		if (force_quit)
+			return;
+		all_ports_up = 1;
+		RTE_ETH_FOREACH_DEV(portid)
+		{
+			if (force_quit)
+				return;
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(portid, &link);
+			/* Print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status)
+					printf("Port%d Link Up. Speed %u Mbps "
+					       "-%s\n",
+					       portid, link.link_speed,
+					       (link.link_duplex ==
+						ETH_LINK_FULL_DUPLEX)
+						       ? ("full-duplex")
+						       : ("half-duplex\n"));
+				else
+					printf("Port %d Link Down\n", portid);
+				continue;
+			}
+			/* Clear all_ports_up flag if any link down */
+			if (link.link_status == ETH_LINK_DOWN) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* After finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* Set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("Done\n");
+		}
+	}
+}
+
 static void
 signal_handler(int signum)
 {
@@ -483,7 +627,14 @@ signal_handler(int signum)
 int
 main(int argc, char **argv)
 {
-	uint16_t portid;
+	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_eth_dev_info dev_info;
+	uint32_t n_tx_queue, nb_lcores;
+	struct rte_eth_txconf *txconf;
+	uint16_t queueid, portid;
+	struct lcore_conf *qconf;
+	uint32_t lcore_id;
+	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -519,6 +670,203 @@ main(int argc, char **argv)
 	if (check_port_config() < 0)
 		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
 
+	nb_ports = rte_eth_dev_count_avail();
+	nb_lcores = rte_lcore_count();
+
+	/* Initialize all ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		struct rte_eth_conf local_port_conf = port_conf;
+
+		/* Skip ports that are not enabled */
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("\nSkipping disabled port %d\n", portid);
+			continue;
+		}
+
+		/* Init port */
+		printf("Initializing port %d ... ", portid);
+		fflush(stdout);
+
+		nb_rx_queue = get_port_n_rx_queues(portid);
+		n_tx_queue = nb_lcores;
+		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
+			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
+		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
+		       nb_rx_queue, n_tx_queue);
+
+		rte_eth_dev_info_get(portid, &dev_info);
+		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+			local_port_conf.txmode.offloads |=
+				DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
+			dev_info.flow_type_rss_offloads;
+		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
+		    port_conf.rx_adv_conf.rss_conf.rss_hf) {
+			printf("Port %u modified RSS hash function based on "
+			       "hardware support,"
+			       "requested:%#" PRIx64 " configured:%#" PRIx64
+			       "\n",
+			       portid, port_conf.rx_adv_conf.rss_conf.rss_hf,
+			       local_port_conf.rx_adv_conf.rss_conf.rss_hf);
+		}
+
+		ret = rte_eth_dev_configure(portid, nb_rx_queue,
+					    n_tx_queue, &local_port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot configure device: err=%d, port=%d\n",
+				 ret, portid);
+
+		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
+						       &nb_txd);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot adjust number of descriptors: err=%d, "
+				 "port=%d\n",
+				 ret, portid);
+
+		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
+		print_ethaddr(" Address:", &ports_eth_addr[portid]);
+		printf(", ");
+		print_ethaddr(
+			"Destination:",
+			(const struct rte_ether_addr *)&dest_eth_addr[portid]);
+		printf(", ");
+
+		/*
+		 * prepare src MACs for each port.
+		 */
+		rte_ether_addr_copy(
+			&ports_eth_addr[portid],
+			(struct rte_ether_addr *)(val_eth + portid) + 1);
+
+		/* Init memory */
+		if (!per_port_pool) {
+			/* portid = 0; this is *not* signifying the first port,
+			 * rather, it signifies that portid is ignored.
+			 */
+			ret = init_mem(0, NB_MBUF(nb_ports));
+		} else {
+			ret = init_mem(portid, NB_MBUF(1));
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "init_mem() failed\n");
+
+		/* Init one TX queue per couple (lcore,port) */
+		queueid = 0;
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			qconf = &lcore_conf[lcore_id];
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
+			fflush(stdout);
+
+			txconf = &dev_info.default_txconf;
+			txconf->offloads = local_port_conf.txmode.offloads;
+			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+						     socketid, txconf);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_tx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+			queueid++;
+		}
+
+		printf("\n");
+	}
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		qconf = &lcore_conf[lcore_id];
+		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
+		fflush(stdout);
+		/* Init RX queues */
+		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+			struct rte_eth_rxconf rxq_conf;
+
+			portid = qconf->rx_queue_list[queue].port_id;
+			queueid = qconf->rx_queue_list[queue].queue_id;
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
+			fflush(stdout);
+
+			rte_eth_dev_info_get(portid, &dev_info);
+			rxq_conf = dev_info.default_rxconf;
+			rxq_conf.offloads = port_conf.rxmode.offloads;
+			if (!per_port_pool)
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf, pktmbuf_pool[0][socketid]);
+			else
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf,
+					pktmbuf_pool[portid][socketid]);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_rx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+
+		}
+	}
+
+	printf("\n");
+
+	/* Start ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* Start device */
+		ret = rte_eth_dev_start(portid);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "rte_eth_dev_start: err=%d, port=%d\n", ret,
+				 portid);
+
+		/*
+		 * If enabled, put device in promiscuous mode.
+		 * This allows IO forwarding mode to forward packets
+		 * to itself through 2 cross-connected  ports of the
+		 * target machine.
+		 */
+		if (promiscuous_on)
+			rte_eth_promiscuous_enable(portid);
+	}
+
+	printf("\n");
+
+	check_all_ports_link_status(enabled_port_mask);
+
+	/* Stop ports */
+	RTE_ETH_FOREACH_DEV(portid) {
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("Closing port %d...", portid);
+		rte_eth_dev_stop(portid);
+		rte_eth_dev_close(portid);
+		printf(" Done\n");
+	}
 	printf("Bye...\n");
 
 	return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 27/29] l3fwd-graph: add graph config and main loop
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (25 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 26/29] l3fwd-graph: add ethdev configuration changes jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 28/29] doc: add graph library programmer's guide guide jerinj
                       ` (2 subsequent siblings)
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph creation, configuration logic and graph main loop.
This graph main loop is run on every slave lcore and calls
rte_graph_walk() to walk over lcore specific rte_graph.
Master core accumulates and prints graph walk stats of all the
lcore's graph's.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 242 +++++++++++++++++++++++++++++++++++-
 1 file changed, 240 insertions(+), 2 deletions(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index 47d2f2ecb..28db947b5 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -23,9 +23,13 @@
 #include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_graph_worker.h>
+#include <rte_launch.h>
 #include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
+#include <rte_node_eth_api.h>
+#include <rte_node_ip4_api.h>
 #include <rte_per_lcore.h>
 #include <rte_string_fns.h>
 #include <rte_vect.h>
@@ -75,12 +79,17 @@ static uint32_t enabled_port_mask;
 struct lcore_rx_queue {
 	uint16_t port_id;
 	uint8_t queue_id;
+	char node_name[RTE_NODE_NAMESIZE];
 };
 
 /* Lcore conf */
 struct lcore_conf {
 	uint16_t n_rx_queue;
 	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+
+	struct rte_graph *graph;
+	char name[RTE_GRAPH_NAMESIZE];
+	rte_graph_t graph_id;
 } __rte_cache_aligned;
 
 static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
@@ -119,6 +128,25 @@ static struct rte_eth_conf port_conf = {
 
 static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
 
+static struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS];
+
+struct ipv4_l3fwd_lpm_route {
+	uint32_t ip;
+	uint8_t depth;
+	uint8_t if_out;
+};
+
+#define IPV4_L3FWD_LPM_NUM_ROUTES                                              \
+	(sizeof(ipv4_l3fwd_lpm_route_array) /                                  \
+	 sizeof(ipv4_l3fwd_lpm_route_array[0]))
+/* 198.18.0.0/16 are set aside for RFC2544 benchmarking. */
+static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
+	{RTE_IPV4(198, 18, 0, 0), 24, 0}, {RTE_IPV4(198, 18, 1, 0), 24, 1},
+	{RTE_IPV4(198, 18, 2, 0), 24, 2}, {RTE_IPV4(198, 18, 3, 0), 24, 3},
+	{RTE_IPV4(198, 18, 4, 0), 24, 4}, {RTE_IPV4(198, 18, 5, 0), 24, 5},
+	{RTE_IPV4(198, 18, 6, 0), 24, 6}, {RTE_IPV4(198, 18, 7, 0), 24, 7},
+};
+
 static int
 check_lcore_params(void)
 {
@@ -624,17 +652,87 @@ signal_handler(int signum)
 	}
 }
 
+static void
+print_stats(void)
+{
+	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+	const char clr[] = {27, '[', '2', 'J', '\0'};
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker_*";
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL)
+		rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
+
+	while (!force_quit) {
+		/* Clear screen and move to top left */
+		printf("%s%s", clr, topLeft);
+		rte_graph_cluster_stats_get(stats, 0);
+		rte_delay_ms(1E3);
+	}
+
+	rte_graph_cluster_stats_destroy(stats);
+}
+
+/* Main processing loop */
+static int
+graph_main_loop(void *conf)
+{
+	struct lcore_conf *qconf;
+	struct rte_graph *graph;
+	uint32_t lcore_id;
+
+	RTE_SET_USED(conf);
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_conf[lcore_id];
+	graph = qconf->graph;
+
+	if (!graph) {
+		RTE_LOG(INFO, L3FWD_GRAPH, "Lcore %u has nothing to do\n",
+			lcore_id);
+		return 0;
+	}
+
+	RTE_LOG(INFO, L3FWD_GRAPH,
+		"Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+		qconf->name, graph);
+
+	while (likely(!force_quit))
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
 int
 main(int argc, char **argv)
 {
+	uint8_t rewrite_data[2 * sizeof(struct rte_ether_addr)];
+	static const char *node_patterns[] = {
+		"ip4*",
+		"ethdev_tx-*",
+		"pkt_drop",
+	};
 	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_graph_param graph_conf;
 	struct rte_eth_dev_info dev_info;
+	uint32_t nb_ports, nb_conf = 0;
 	uint32_t n_tx_queue, nb_lcores;
 	struct rte_eth_txconf *txconf;
-	uint16_t queueid, portid;
+	uint16_t queueid, portid, i;
 	struct lcore_conf *qconf;
+	uint16_t nb_graphs = 0;
+	uint16_t nb_patterns;
+	uint8_t rewrite_len;
 	uint32_t lcore_id;
-	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -783,6 +881,18 @@ main(int argc, char **argv)
 			queueid++;
 		}
 
+		/* Setup ethdev node config */
+		ethdev_conf[nb_conf].port_id = portid;
+		ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+		ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+		if (!per_port_pool)
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+
+		else
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+		ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+		nb_conf++;
 		printf("\n");
 	}
 
@@ -826,11 +936,26 @@ main(int argc, char **argv)
 					 "port=%d\n",
 					 ret, portid);
 
+			/* Add this queue node to its graph */
+			snprintf(qconf->rx_queue_list[queue].node_name,
+				 RTE_NODE_NAMESIZE, "ethdev_rx-%u-%u", portid,
+				 queueid);
+		}
+
+		/* Alloc a graph to this lcore only if source exists  */
+		if (qconf->n_rx_queue) {
+			qconf->graph_id = nb_graphs;
+			nb_graphs++;
 		}
 	}
 
 	printf("\n");
 
+	/* Ethdev node config, skip rx queue mapping */
+	ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+	if (ret)
+		rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", ret);
+
 	/* Start ports */
 	RTE_ETH_FOREACH_DEV(portid)
 	{
@@ -858,6 +983,119 @@ main(int argc, char **argv)
 
 	check_all_ports_link_status(enabled_port_mask);
 
+	/* Graph Initialization */
+	memset(&graph_conf, 0, sizeof(graph_conf));
+	graph_conf.node_patterns = node_patterns;
+	nb_patterns = RTE_DIM(node_patterns);
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		rte_graph_t graph_id;
+		rte_edge_t i;
+
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		qconf = &lcore_conf[lcore_id];
+
+		/* Skip graph creation if no source exists */
+		if (!qconf->n_rx_queue)
+			continue;
+
+		/* Add rx node patterns of this lcore */
+		for (i = 0; i < qconf->n_rx_queue; i++) {
+			graph_conf.node_patterns[nb_patterns + i] =
+				qconf->rx_queue_list[i].node_name;
+		}
+
+		graph_conf.nb_node_patterns = nb_patterns + i;
+		graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+		snprintf(qconf->name, sizeof(qconf->name), "worker_%u",
+			 lcore_id);
+
+		graph_id = rte_graph_create(qconf->name, &graph_conf);
+		if (graph_id != qconf->graph_id)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_create(): graph_id=%d not "
+				 " as expected for lcore %u(%u\n",
+				 graph_id, lcore_id, qconf->graph_id);
+
+		qconf->graph = rte_graph_lookup(qconf->name);
+		if (!qconf->graph)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_lookup(): graph %s not found\n",
+				 qconf->name);
+	}
+
+	memset(&rewrite_data, 0, sizeof(rewrite_data));
+	rewrite_len = sizeof(rewrite_data);
+
+	/* Add route to ip4 graph infra */
+	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+		char route_str[INET6_ADDRSTRLEN * 4];
+		char abuf[INET6_ADDRSTRLEN];
+		struct in_addr in;
+		uint32_t dst_port;
+		uint16_t next_hop;
+
+		/* Skip unused ports */
+		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
+		     enabled_port_mask) == 0)
+			continue;
+
+		dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+		next_hop = i;
+
+		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
+		snprintf(route_str, sizeof(route_str), "%s / %d (%d)",
+			 inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
+			 ipv4_l3fwd_lpm_route_array[i].depth,
+			 ipv4_l3fwd_lpm_route_array[i].if_out);
+
+		ret = rte_node_ip4_route_add(
+			ipv4_l3fwd_lpm_route_array[i].ip,
+			ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+			RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add ip4 route %s to graph\n",
+				 route_str);
+
+		memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+		/* Add next hop for a given destination */
+		ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+					       rewrite_len, dst_port);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add next hop %u for "
+				 "route %s\n",
+				 next_hop, route_str);
+
+		RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+			route_str, next_hop);
+	}
+
+	/* Launch per-lcore init on every slave lcore */
+	rte_eal_mp_remote_launch(graph_main_loop, NULL, SKIP_MASTER);
+
+	/* Accumulate and print stats on master until exit */
+	if (rte_graph_has_stats_feature())
+		print_stats();
+
+	/* Wait for slave cores to exit */
+	ret = 0;
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		ret = rte_eal_wait_lcore(lcore_id);
+		/* Destroy graph */
+		rte_graph_destroy(lcore_conf[lcore_id].name);
+		if (ret < 0) {
+			ret = -1;
+			break;
+		}
+	}
+
 	/* Stop ports */
 	RTE_ETH_FOREACH_DEV(portid) {
 		if ((enabled_port_mask & (1 << portid)) == 0)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 28/29] doc: add graph library programmer's guide guide
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (26 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 27/29] l3fwd-graph: add graph config and main loop jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 29/29] doc: add l3fwd graph application user guide jerinj
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Adding programmer's guide for Graph library and the inbuilt nodes.
This patch also updates the release note for the new libraries.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   24 +
 6 files changed, 5532 insertions(+)
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg

diff --git a/doc/guides/prog_guide/graph_lib.rst b/doc/guides/prog_guide/graph_lib.rst
new file mode 100644
index 000000000..17101b10a
--- /dev/null
+++ b/doc/guides/prog_guide/graph_lib.rst
@@ -0,0 +1,397 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+Graph Library and Inbuilt Nodes
+===============================
+
+Graph architecture abstracts the data processing functions as a ``node`` and
+``links`` them together to create a complex ``graph`` to enable reusable/modular
+data processing functions.
+
+The graph library provides API to enable graph framework operations such as
+create, lookup, dump and destroy on graph and node operations such as clone,
+edge update, and edge shrink, etc. The API also allows to create the stats
+cluster to monitor per graph and per node stats.
+
+Features
+--------
+
+Features of the Graph library are:
+
+- Nodes as plugins.
+- Support for out of tree nodes.
+- Inbuilt nodes for packet processing.
+- Multi-process support.
+- Low overhead graph walk and node enqueue.
+- Low overhead statistics collection infrastructure.
+- Support to export the graph as a Graphviz dot file. See ``rte_graph_export()``.
+- Allow having another graph walk implementation in the future by segregating
+  the fast path(``rte_graph_worker.h``) and slow path code.
+
+Advantages of Graph architecture
+--------------------------------
+
+- Memory latency is the enemy for high-speed packet processing, moving the
+  similar packet processing code to a node will reduce the I cache and D
+  caches misses.
+- Exploits the probability that most packets will follow the same nodes in the
+  graph.
+- Allow SIMD instructions for packet processing of the node.-
+- The modular scheme allows having reusable nodes for the consumers.
+- The modular scheme allows us to abstract the vendor HW specific
+  optimizations as a node.
+
+Performance tuning parameters
+-----------------------------
+
+- Test with various burst size values (256, 128, 64, 32) using
+  CONFIG_RTE_GRAPH_BURST_SIZE config option.
+  The testing shows, on x86 and arm64 servers, The sweet spot is 256 burst
+  size. While on arm64 embedded SoCs, it is either 64 or 128.
+- Disable node statistics (using ``CONFIG_RTE_LIBRTE_GRAPH_STATS`` config option)
+  if not needed.
+- Use arm64 optimized memory copy for arm64 architecture by
+  selecting ``CONFIG_RTE_ARCH_ARM64_MEMCPY``.
+
+Programming model
+-----------------
+
+Anatomy of Node:
+~~~~~~~~~~~~~~~~
+
+.. _figure_anatomy_of_a_node:
+
+.. figure:: img/anatomy_of_a_node.*
+
+The :numref:`figure_anatomy_of_a_node` diagram depicts the anatomy of a node.
+
+The node is the basic building block of the graph framework.
+
+A node consists of:
+
+process():
+^^^^^^^^^^
+
+The callback function will be invoked by worker thread using
+``rte_graph_walk()`` function when there is data to be processed by the node.
+A graph node process the function using ``process()`` and enqueue to next
+downstream node using ``rte_node_enqueue*()`` function.
+
+Context memory:
+^^^^^^^^^^^^^^^
+
+It is memory allocated by the library to store the node-specific context
+information. This memory will be used by process(), init(), fini() callbacks.
+
+init():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_create()`` on when
+a node gets attached to a graph.
+
+fini():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_destroy()`` on when a
+node gets detached to a graph.
+
+Node name:
+^^^^^^^^^^
+
+It is the name of the node. When a node registers to graph library, the library
+gives the ID as ``rte_node_t`` type. Both ID or Name shall be used lookup the
+node. ``rte_node_from_name()``, ``rte_node_id_to_name()`` are the node
+lookup functions.
+
+nb_edges:
+^^^^^^^^^
+
+The number of downstream nodes connected to this node. The ``next_nodes[]``
+stores the downstream nodes objects. ``rte_node_edge_update()`` and
+``rte_node_edge_shrink()`` functions shall be used to update the ``next_node[]``
+objects. Consumers of the node APIs are free to update the ``next_node[]``
+objects till ``rte_graph_create()`` invoked.
+
+next_node[]:
+^^^^^^^^^^^^
+
+The dynamic array to store the downstream nodes connected to this node. Downstream
+node should not be current node itself or a source node.
+
+Source node:
+^^^^^^^^^^^^
+
+Source nodes are static nodes created using ``RTE_NODE_REGISTER`` by passing
+``flags`` as ``RTE_NODE_SOURCE_F``.
+While performing the graph walk, the ``process()`` function of all the source
+nodes will be called first. So that these nodes can be used as input nodes for a graph.
+
+Node creation and registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Node implementer creates the node by implementing ops and attributes of
+  ``struct rte_node_register``.
+
+* The library registers the node by invoking RTE_NODE_REGISTER on library load
+  using the constructor scheme. The constructor scheme used here to support multi-process.
+
+Link the Nodes to create the graph topology
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. _figure_link_the_nodes:
+
+.. figure:: img/link_the_nodes.*
+
+The :numref:`figure_link_the_nodes` diagram shows a graph topology after
+linking the N nodes.
+
+Once nodes are available to the program, Application or node public API
+functions can links them together to create a complex packet processing graph.
+
+There are multiple different types of strategies to link the nodes.
+
+Method (a):
+^^^^^^^^^^^
+Provide the ``next_nodes[]`` at the node registration time. See  ``struct rte_node_register::nb_edges``.
+This is a use case to address the static node scheme where one knows upfront the
+``next_nodes[]`` of the node.
+
+Method (b):
+^^^^^^^^^^^
+Use ``rte_node_edge_get()``, ``rte_node_edge_update()``, ``rte_node_edge_shrink()``
+to update the ``next_nodes[]`` links for the node runtime but before graph create.
+
+Method (c):
+^^^^^^^^^^^
+Use ``rte_node_clone()`` to clone a already existing node, created using RTE_NODE_REGISTER.
+When ``rte_node_clone()`` invoked, The library, would clone all the attributes
+of the node and creates a new one. The name for cloned node shall be
+``"parent_node_name-user_provided_name"``.
+
+This method enables the use case of Rx and Tx nodes where multiple of those nodes
+need to be cloned based on the number of CPU available in the system.
+The cloned nodes will be identical, except the ``"context memory"``.
+Context memory will have information of port, queue pair in case of Rx and Tx
+ethdev nodes.
+
+Create the graph object
+~~~~~~~~~~~~~~~~~~~~~~~
+Now that the nodes are linked, Its time to create a graph by including
+the required nodes. The application can provide a set of node patterns to
+form a graph object. The ``famish()`` API used underneath for the pattern
+matching to include the required nodes. After the graph create any changes to
+nodes or graph is not allowed.
+
+The ``rte_graph_create()`` API shall be used to create the graph.
+
+Example of a graph object creation:
+
+.. code-block:: console
+
+   {"ethdev_rx-0-0", ip4*, ethdev_tx-*"}
+
+In the above example, A graph object will be created with ethdev Rx
+node of port 0 and queue 0, all ipv4* nodes in the system,
+and ethdev tx node of all ports.
+
+Multicore graph processing
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+In the current graph library implementation, specifically,
+``rte_graph_walk()`` and ``rte_node_enqueue*`` fast path API functions
+are designed to work on single-core to have better performance.
+The fast path API works on graph object, So the multi-core graph
+processing strategy would be to create graph object PER WORKER.
+
+In fast path
+~~~~~~~~~~~~
+Typical fast-path code looks like below, where the application
+gets the fast-path graph object using ``rte_graph_lookup()``
+on the worker thread and run the ``rte_graph_walk()`` in a tight loop.
+
+.. code-block:: c
+
+    struct rte_graph *graph = rte_graph_lookup("worker0");
+
+    while (!done) {
+        rte_graph_walk(graph);
+    }
+
+Context update when graph walk in action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The fast-path object for the node is ``struct rte_node``.
+
+It may be possible that in slow-path or after the graph walk-in action,
+the user needs to update the context of the node hence access to
+``struct rte_node *`` memory.
+
+``rte_graph_foreach_node()``, ``rte_graph_node_get()``,
+``rte_graph_node_get_by_name()`` APIs can be used to to get the
+``struct rte_node*``. ``rte_graph_foreach_node()`` iterator function works on
+``struct rte_graph *`` fast-path graph object while others works on graph ID or name.
+
+Get the node statistics using graph cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The user may need to know the aggregate stats of the node across
+multiple graph objects. Especially the situation where each graph object bound
+to a worker thread.
+
+Introduced a graph cluster object for statistics.
+``rte_graph_cluster_stats_create()`` API shall be used for creating a
+graph cluster with multiple graph objects and ``rte_graph_cluster_stats_get()``
+to get the aggregate node statistics.
+
+An example statistics output from ``rte_graph_cluster_stats_get()``
+
+.. code-block:: diff
+
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+    |Node     |calls      |objs         |realloc_count  |objs/call  |objs/sec(10E6) |cycles/call|
+    +---------------------+-------------+---------------+-----------+---------------+-----------+
+    |node0    |12977424   |3322220544   |5              |256.000    |3047.151872    |20.0000    |
+    |node1    |12977653   |3322279168   |0              |256.000    |3047.210496    |17.0000    |
+    |node2    |12977696   |3322290176   |0              |256.000    |3047.221504    |17.0000    |
+    |node3    |12977734   |3322299904   |0              |256.000    |3047.231232    |17.0000    |
+    |node4    |12977784   |3322312704   |1              |256.000    |3047.243776    |17.0000    |
+    |node5    |12977825   |3322323200   |0              |256.000    |3047.254528    |17.0000    |
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+
+Node writing guidelines
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``process()`` function of a node is the fast-path function and that needs
+to be written carefully to achieve max performance.
+
+Broadly speaking, there are two different types of nodes.
+
+Static nodes
+~~~~~~~~~~~~
+The first kind of nodes are those that have a fixed ``next_nodes[]`` for the
+complete burst (like ethdev_rx, ethdev_tx) and it is simple to write.
+``process()`` function can move the obj burst to the next node either using
+``rte_node_next_stream_move()`` or using ``rte_node_next_stream_get()`` and
+``rte_node_next_stream_put()``.
+
+Intermediate nodes
+~~~~~~~~~~~~~~~~~~
+The second kind of such node is ``intermediate nodes`` that decide what is the
+``next_node[]`` to send to on a per-packet basis. In these nodes,
+
+* Firstly, there has to be the best possible packet processing logic.
+
+* Secondly, each packet needs to be queued to its next node.
+
+This can be done using ``rte_node_enqueue_[x1|x2|x4]()`` APIs if
+they are to single next or ``rte_node_enqueue_next()`` that takes array of nexts.
+
+In scenario where multiple intermediate nodes are present but most of the time
+each node using the same next node for all its packets, the cost of moving every
+pointer from current node's stream to next node's stream could be avoided.
+This is called home run and ``rte_node_next_stream_move()`` could be used to
+just move stream from the current node to the next node with least number of cycles.
+Since this can be avoided only in the case where all the packets are destined
+to the same next node, node implementation should be also having worst-case
+handling where every packet could be going to different next node.
+
+Example of intermediate node implementation with home run:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1. Start with speculation that next_node = node->ctx.
+This could be the next_node application used in the previous function call of this node.
+
+2. Get the next_node stream array with required space using
+``rte_node_next_stream_get(next_node, space)``.
+
+3. while n_left_from > 0 (i.e packets left to be sent) prefetch next pkt_set
+and process current pkt_set to find their next node
+
+4. if all the next nodes of the current pkt_set match speculated next node,
+just count them as successfully speculated(``last_spec``) till now and
+continue the loop without actually moving them to the next node. else if there is
+a mismatch, copy all the pkt_set pointers that were ``last_spec`` and move the
+current pkt_set to their respective next's nodes using ``rte_enqueue_next_x1()``.
+Also, one of the next_node can be updated as speculated next_node if it is more
+probable. Finally, reset ``last_spec`` to zero.
+
+5. if n_left_from != 0 then goto 3) to process remaining packets.
+
+6. if last_spec == nb_objs, All the objects passed were successfully speculated
+to single next node. So, the current stream can be moved to next node using
+``rte_node_next_stream_move(node, next_node)``.
+This is the ``home run`` where memcpy of buffer pointers to next node is avoided.
+
+7. Update the ``node->ctx`` with more probable next node.
+
+Graph object memory layout
+--------------------------
+.. _figure_graph_mem_layout:
+
+.. figure:: img/graph_mem_layout.*
+
+The :numref:`figure_graph_mem_layout` diagram shows ``rte_graph`` object memory
+layout. Understanding the memory layout helps to debug the graph library and
+improve the performance if needed.
+
+Graph object consists of a header, circular buffer to store the pending
+stream when walking over the graph, and variable-length memory to store
+the ``rte_node`` objects.
+
+The graph_nodes_mem_create() creates and populate this memory. The functions
+such as ``rte_graph_walk()`` and ``rte_node_enqueue_*`` use this memory
+to enable fastpath services.
+
+Inbuilt Nodes
+-------------
+
+DPDK provides a set of nodes for data processing. The following section
+details the documentation for the same.
+
+ethdev_rx
+~~~~~~~~~
+This node does ``rte_eth_rx_burst()`` into stream buffer passed to it
+(src node stream) and does ``rte_node_next_stream_move()`` only when
+there are packets received. Each ``rte_node`` works only on one Rx port and
+queue that it gets from node->ctx. For each (port X, rx_queue Y),
+a rte_node is cloned from  ethdev_rx_base_node as ``ethdev_rx-X-Y`` in
+``rte_node_eth_config()`` along with updating ``node->ctx``.
+Each graph needs to be associated  with a unique rte_node for a (port, rx_queue).
+
+ethdev_tx
+~~~~~~~~~
+This node does ``rte_eth_tx_burst()`` for a burst of objs received by it.
+It sends the burst to a fixed Tx Port and Queue information from
+node->ctx. For each (port X), this ``rte_node`` is cloned from
+ethdev_tx_node_base as "ethdev_tx-X" in ``rte_node_eth_config()``
+along with updating node->context.
+
+Since each graph doesn't need more than one Txq, per port, a Txq is assigned
+based on graph id to each rte_node instance. Each graph needs to be associated
+with a rte_node for each (port).
+
+pkt_drop
+~~~~~~~~
+This node frees all the objects passed to it considering them as
+``rte_mbufs`` that need to be freed.
+
+ip4_lookup
+~~~~~~~~~~
+This node is an intermediate node that does LPM lookup for the received
+ipv4 packets and the result determines each packets next node.
+
+On successful LPM lookup, the result contains the ``next_node`` id and
+``next-hop`` id with which the packet needs to be further processed.
+
+On LPM lookup failure, objects are redirected to pkt_drop node.
+``rte_node_ip4_route_add()`` is control path API to add ipv4 routes.
+To achieve home run, node use ``rte_node_stream_move()`` as mentioned in above
+sections.
+
+ip4_rewrite
+~~~~~~~~~~~
+This node gets packets from ``ip4_lookup`` node with next-hop id for each
+packet is embedded in ``rte_node_mbuf_priv1(mbuf)->nh``. This id is used
+to determine the L2 header to be written to the packet before sending
+the packet out to a particular ethdev_tx node.
+``rte_node_ip4_rewrite_add()`` is control path API to add next-hop info.
+
+null
+~~~~
+This node ignores the set of objects passed to it and reports that all are
+processed.
+
diff --git a/doc/guides/prog_guide/img/anatomy_of_a_node.svg b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
new file mode 100644
index 000000000..fa4b5b2d5
--- /dev/null
+++ b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
@@ -0,0 +1,1078 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg419"
+   sodipodi:docname="anatomy_of_a_node.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata425">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs423" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview421"
+     showgrid="false"
+     inkscape:zoom="2.406782"
+     inkscape:cx="470.64353"
+     inkscape:cy="284.06748"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg419" />
+  <clipPath
+     id="p.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path10" />
+  </clipPath>
+  <g
+     clip-path="url(#p.0)"
+     id="g417"
+     transform="matrix(1.0160138,0,0,1.0169275,-5.7394334,-5.6337913)">
+    <path
+       d="M 0,0 H 960 V 540 H 0 Z"
+       id="path13"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path15"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path17"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#741b47;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path19"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path21"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#c27ba0;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:4, 3, 1, 3" />
+    <path
+       d="M 540.8504,238.14069 V 415.12232"
+       id="path23"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="M 619.6588,238.14069 V 415.12232"
+       id="path25"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,238.63937 h 79.80579"
+       id="path27"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,273.8362 h 79.80579"
+       id="path29"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,309.03308 h 79.80579"
+       id="path31"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,344.22992 h 79.80579"
+       id="path33"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,379.42676 h 79.80579"
+       id="path35"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,414.62363 h 79.80579"
+       id="path37"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 559.7812,260.43936 v -9.54686 h 1.29687 l 5.01563,7.49998 v -7.49998 h 1.20312 v 9.54686 h -1.29687 l -5.01563,-7.49998 v 7.49998 z m 9.04706,-3.45312 q 0,-1.92186 1.07812,-2.84374 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57811 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28123 -0.59375,-1.93748 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98436 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03124 0.34375,-1.87499 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.
 79687,0.67188 v -3.42188 h 1.17188 v 9.54686 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42186 -0.54687,-2.07811 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,1.99999 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.74999 0.89063,-2.70311 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65623 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85936 -0.4375,-1.29686 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.43748 z m 9.89667,-0.57811 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.68
 75,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.29688 0.95313,0.89063 0.375,0.57812 0.59375,1.42187 0.21875,0.82813 0.21875,2.25 0,1.67186 -0.35938,2.70311 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.98438 -0.95313,-1.1875 -0.95313,-3.87498 z m 1.20313,0 q 0,2.34373 0.54687,3.12498 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.12498 0,-2.35937 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path39"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,295.63623 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.
 42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 H 598.128 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.1406
 2 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path41"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,330.83307 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29687 q -0.0156,-0.42187 0.14062,-0.8125 0.23438,-0.64062 0.76563,-1.26562 0.53125,-0.625
  1.53125,-1.45313 1.5625,-1.26562 2.10937,-2.01562 0.54688,-0.75 0.54688,-1.40625 0,-0.70313 -0.5,-1.17188 -0.5,-0.48437 -1.29688,-0.48437 -0.85937,0 -1.375,0.51562 -0.5,0.5 -0.5,1.39063 l -1.20312,-0.10938 q 0.125,-1.35937 0.92187,-2.0625 0.8125,-0.70312 2.17188,-0.70312 1.375,0 2.17187,0.76562 0.8125,0.75 0.8125,1.875 0,0.57813 -0.23437,1.14063 -0.23438,0.54687 -0.78125,1.15625 -0.54688,0.60937 -1.8125,1.67187 -1.04688,0.89063 -1.35938,1.21875 -0.29687,0.3125 -0.48437,0.625 z"
+       id="path43"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 577.7547,366.0299 v -1.32813 h 1.34375 v 1.32813 z m 3.703,0 v -1.32813 h 1.34375 v 1.32813 z"
+       id="path45"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,401.22678 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98438 q 0.75,-1.14063 2.1875,-1.14063 0.625,0 1.15625,0.21875 0.53125,0.218
 75 0.78125,0.59375 0.26562,0.35938 0.375,0.85938 0.0625,0.32812 0.0625,1.14062 v 4.25 h -1.17188 v -4.20312 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35938 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79687 v 3.78125 z"
+       id="path47"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path49"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path51"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 598.25494,126.88399 v -9.54688 h 1.29688 l 5.01562,7.5 v -7.5 h 1.20313 v 9.54688 h -1.29688 l -5.01562,-7.5 v 7.5 z m 9.047,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.4
 2188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z"
+       id="path53"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 596.41,142.88399 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 14.00012,-0.85938 q -0.65625,0.5625 -1.26562,0.79688 -0.59375,0.21875 -1.28125,0.21875 -1.14063,0 -1.75,-0.54688 -0.60938,-0.5625 -0.60938,-1.4375 0,-0.5 0.21875,-0.92187 0.23438,-0.42188 0.60938,-0.67188 0.375,-0.25 0.84375,-0.39062 0.34375,-0.0781 1.04687,-0.17188 1.42188,-0.17187 2.09375,-0.40625 0,-0.23437 0,-0.29687 0,-0.71875 -0.32812,-1.01563 -0.45313,-0.39062 -1.34375,-0.39062 -0.8125,0 -1.21875,0.29687 -0.39063,0.28125 -0.57813,1.01563 l -1.14062,-0.15625 q 0.15625,-0.73438 0.51562,-1.1875 0.35938,-0.45313 1.03125,-0.6875 0.67188,-0.25 1.5625,-0.25 0.89063,0 1.4375,0.20312 0.5625,0.20313 0.8125,0.53125 0.26563,0.3125 0.375,0.79688 0.0469,0.29687 0.0469,1.07812 v 1.5625 q 0,1.625 0.0781,2.0625 0.0781,0.4375 0.29688,0.82813 h -1.21875 q -0.1875,-0.35938 -0.23438,-0.85938 z m -0.0937,-2.60937 q -0.64062,0.26562 -1.92187,0.4375 -0.71875,0.10937
  -1.01563,0.25 -0.29687,0.125 -0.46875,0.375 -0.15625,0.25 -0.15625,0.54687 0,0.46875 0.34375,0.78125 0.35938,0.3125 1.04688,0.3125 0.67187,0 1.20312,-0.29687 0.53125,-0.29688 0.78125,-0.8125 0.1875,-0.39063 0.1875,-1.17188 z m 2.9906,3.46875 v -6.90625 h 1.04688 v 0.96875 q 0.32812,-0.51563 0.85937,-0.8125 0.54688,-0.3125 1.23438,-0.3125 0.78125,0 1.26562,0.3125 0.48438,0.3125 0.6875,0.89062 0.82813,-1.20312 2.14063,-1.20312 1.03125,0 1.57812,0.57812 0.5625,0.5625 0.5625,1.73438 v 4.75 h -1.17187 v -4.35938 q 0,-0.70312 -0.125,-1 -0.10938,-0.3125 -0.40625,-0.5 -0.29688,-0.1875 -0.70313,-0.1875 -0.71875,0 -1.20312,0.48438 -0.48438,0.48437 -0.48438,1.54687 v 4.01563 h -1.17187 v -4.48438 q 0,-0.78125 -0.29688,-1.17187 -0.28125,-0.39063 -0.92187,-0.39063 -0.5,0 -0.92188,0.26563 -0.42187,0.25 -0.60937,0.75 -0.1875,0.5 -0.1875,1.45312 v 3.57813 z m 15.83686,-2.21875 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0
 .89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z"
+       id="path55"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path57"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path59"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 186.03761,297.92676 v -11.48438 h 1.28125 v 1.07813 q 0.45312,-0.64063 1.01562,-0.95313 0.57813,-0.3125 1.39063,-0.3125 1.0625,0 1.875,0.54688 0.8125,0.54687 1.21875,1.54687 0.42187,0.98438 0.42187,2.17188 0,1.28125 -0.46875,2.29687 -0.45312,1.01563 -1.32812,1.5625 -0.85938,0.54688 -1.82813,0.54688 -0.70312,0 -1.26562,-0.29688 -0.54688,-0.29687 -0.90625,-0.75 v 4.04688 z m 1.26562,-7.29688 q 0,1.60938 0.64063,2.375 0.65625,0.76563 1.57812,0.76563 0.9375,0 1.60938,-0.79688 0.67187,-0.79687 0.67187,-2.45312 0,-1.59375 -0.65625,-2.375 -0.65625,-0.79688 -1.5625,-0.79688 -0.89062,0 -1.59375,0.84375 -0.6875,0.84375 -0.6875,2.4375 z m 7.61719,4.10938 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 4.8125,-4.15625 q 0,-2.29688 1.28125,-3.
 40625 1.07813,-0.92188 2.60938,-0.92188 1.71875,0 2.79687,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48437,2.51563 -0.48438,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73438,0 -2.8125,-1.10938 -1.07813,-1.125 -1.07813,-3.23437 z m 1.45313,0 q 0,1.59375 0.6875,2.39062 0.70312,0.79688 1.75,0.79688 1.04687,0 1.73437,-0.79688 0.70313,-0.79687 0.70313,-2.4375 0,-1.53125 -0.70313,-2.32812 -0.6875,-0.79688 -1.73437,-0.79688 -1.04688,0 -1.75,0.79688 -0.6875,0.78125 -0.6875,2.375 z m 13.38281,1.10937 1.39062,0.1875 q -0.23437,1.42188 -1.17187,2.23438 -0.92188,0.8125 -2.28125,0.8125 -1.70313,0 -2.75,-1.10938 -1.03125,-1.125 -1.03125,-3.20312 0,-1.34375 0.4375,-2.34375 0.45312,-1.01563 1.35937,-1.51563 0.92188,-0.5 1.98438,-0.5 1.35937,0 2.21875,0.6875 0.85937,0.67188 1.09375,1.9375 l -1.35938,0.20313 q -0.20312,-0.82813 -0.70312,-1.25 -0.48438,-0.42188 -1.1875,-0.42188 -1.0625,0 -1.73438,0.76563 -0.65625,0.75 -0.65625,2.40625 0,1.67187 0.64063,2.4375 0.64062,0.75 1.67187,0.
 75 0.82813,0 1.375,-0.5 0.5625,-0.51563 0.70313,-1.57813 z m 8.26562,0.375 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.27344,2.46875 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 
 0.25,-0.46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 8,0 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 0.25,-0.
 46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 11.25,5.85938 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375
 ,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path61"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path63"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path65"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 372.79996,224.29451 1.51562,0.375 q -0.46875,1.875 -1.71875,2.85937 -1.23437,0.98438 -3.01562,0.98438 -1.85938,0 -3.01563,-0.75 -1.15625,-0.76563 -1.76562,-2.1875 -0.60938,-1.4375 -0.60938,-3.07813 0,-1.79687 0.6875,-3.125 0.6875,-1.32812 1.9375,-2.01562 1.26563,-0.70313 2.78125,-0.70313 1.71875,0 2.89063,0.875 1.17187,0.875 1.64062,2.46875 l -1.5,0.34375 q -0.39062,-1.25 -1.15625,-1.8125 -0.75,-0.57812 -1.90625,-0.57812 -1.3125,0 -2.20312,0.64062 -0.89063,0.625 -1.25,1.70313 -0.35938,1.0625 -0.35938,2.1875 0,1.46875 0.42188,2.5625 0.4375,1.07812 1.32812,1.625 0.90625,0.53125 1.95313,0.53125 1.26562,0 2.14062,-0.73438 0.89063,-0.73437 1.20313,-2.17187 z m 2.67969,-0.14063 q 0,-2.29687 1.28125,-3.40625 1.07812,-0.92187 2.60937,-0.92187 1.71875,0 2.79688,1.125 1.09375,1.10937 1.09375,3.09375 0,1.59375 -0.48438,2.51562 -0.48437,0.92188 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10937 -1.07812,-1.125 -1.07812,-3.23438 z m 1.45312,0 q 0,1.59375 0.6875,2
 .39063 0.70313,0.79687 1.75,0.79687 1.04688,0 1.73438,-0.79687 0.70312,-0.79688 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32813 -0.6875,-0.79687 -1.73438,-0.79687 -1.04687,0 -1.75,0.79687 -0.6875,0.78125 -0.6875,2.375 z m 7.97656,4.15625 v -8.29687 h 1.26563 v 1.17187 q 0.90625,-1.35937 2.64062,-1.35937 0.75,0 1.375,0.26562 0.625,0.26563 0.9375,0.70313 0.3125,0.4375 0.4375,1.04687 0.0781,0.39063 0.0781,1.35938 v 5.10937 h -1.40625 v -5.04687 q 0,-0.85938 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57812 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 11.96094,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.59375,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 
 0.60937,-0.0625 z m 7.05469,-1.40625 1.45312,0.17187 q -0.34375,1.28125 -1.28125,1.98438 -0.92187,0.70312 -2.35937,0.70312 -1.82813,0 -2.89063,-1.125 -1.0625,-1.125 -1.0625,-3.14062 0,-2.09375 1.07813,-3.25 1.07812,-1.15625 2.79687,-1.15625 1.65625,0 2.70313,1.14062 1.0625,1.125 1.0625,3.17188 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76562,2.10937 0.70313,0.71875 1.73438,0.71875 0.78125,0 1.32812,-0.40625 0.54688,-0.40625 0.85938,-1.29687 z m -4.60938,-2.28125 h 4.625 q -0.0937,-1.04688 -0.53125,-1.5625 -0.67187,-0.8125 -1.73437,-0.8125 -0.96875,0 -1.64063,0.65625 -0.65625,0.64062 -0.71875,1.71875 z m 6.89844,4.95312 3.03125,-4.3125 -2.8125,-3.98437 h 1.76563 l 1.26562,1.9375 q 0.35938,0.5625 0.57813,0.9375 0.34375,-0.51563 0.64062,-0.92188 l 1.39063,-1.95312 h 1.6875 l -2.875,3.90625 3.09375,4.39062 h -1.73438 l -1.70312,-2.57812 -0.45313,-0.70313 -2.17187,3.28125 z m 12,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.593
 75,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z"
+       id="path67"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 364.5812,247.31013 V 235.857 h 2.28125 l 2.71875,8.10938 q 0.375,1.125 0.54688,1.6875 0.1875,-0.625 0.60937,-1.82813 l 2.73438,-7.96875 h 2.04687 v 11.45313 h -1.46875 v -9.59375 l -3.32812,9.59375 h -1.35938 l -3.3125,-9.75 v 9.75 z m 18.875,-2.67188 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.83593,4.95313 v -8.29688 h 1.25 v 1.15625 q 0.39063,-0.60937 1.03125,-0.96875 0.65625,-0.375 1.48438,-0.375 0.92187,0 1.51562,0.39063 0.59375,0.375 0
 .82813,1.0625 0.98437,-1.45313 2.5625,-1.45313 1.23437,0 1.89062,0.6875 0.67188,0.67188 0.67188,2.09375 v 5.70313 h -1.39063 v -5.23438 q 0,-0.84375 -0.14062,-1.20312 -0.14063,-0.375 -0.5,-0.59375 -0.35938,-0.23438 -0.84375,-0.23438 -0.875,0 -1.45313,0.57813 -0.57812,0.57812 -0.57812,1.85937 v 4.82813 h -1.40625 v -5.39063 q 0,-0.9375 -0.34375,-1.40625 -0.34375,-0.46875 -1.125,-0.46875 -0.59375,0 -1.09375,0.3125 -0.5,0.3125 -0.73438,0.92188 -0.21875,0.59375 -0.21875,1.71875 v 4.3125 z m 12.79688,-4.15625 q 0,-2.29688 1.28125,-3.40625 1.07812,-0.92188 2.60937,-0.92188 1.71875,0 2.79688,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48438,2.51563 -0.48437,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10938 -1.07812,-1.125 -1.07812,-3.23437 z m 1.45312,0 q 0,1.59375 0.6875,2.39062 0.70313,0.79688 1.75,0.79688 1.04688,0 1.73438,-0.79688 0.70312,-0.79687 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32812 -0.6875,-0.79688 -1.73438,-0.79688 -1.04687,0 -1.75,0.7968
 8 -0.6875,0.78125 -0.6875,2.375 z m 7.96094,4.15625 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 5.28125,3.20312 -0.15625,-1.32812 q 0.45313,0.125 0.79688,0.125 0.46875,0 0.75,-0.15625 0.28125,-0.15625 0.46875,-0.4375 0.125,-0.20313 0.42187,-1.04688 0.0469,-0.10937 0.125,-0.34375 l -3.14062,-8.3125 h 1.51562 l 1.71875,4.79688 q 0.34375,0.92187 0.60938,1.92187 0.23437,-0.96875 0.57812,-1.89062 l 1.76563,-4.82813 h 1.40625 l -3.15625,8.4375 q -0.5,1.375 -0.78125,1.89063 -0.375,0.6875 -0.85938,1.01562 -0.48437,0.32813 -1.15625,0.32813 -0.40625,0 -0.90625,-0.17188 z"
+       id="path69"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path71"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path73"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 330.8464,371.3732 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55469,0 v -8.29688 h 1.26562 v 1.17188 q 0.90625,-1.35938 2.64063,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17187,-1.28125 -0.15625,-0.4375 -0.57813,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64062,0.5625 -0.64062,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.61718,-1.26563 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23437 -0.42188,-0.25 -0.59375,-0.64063 -0.17188,-0.40625 -0.17188,-1.67187 v -4.76563 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60938 0.0625,0.78125 0.0781,0.17188 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z m
  4.07032,4.64063 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path75"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path77"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path79"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 418.20865,381.21695 v -7.20313 h -1.23438 v -1.09375 h 1.23438 v -0.89062 q 0,-0.82813 0.15625,-1.23438 0.20312,-0.54687 0.70312,-0.89062 0.51563,-0.34375 1.4375,-0.34375 0.59375,0 1.3125,0.14062 l -0.20312,1.23438 q -0.4375,-0.0781 -0.82813,-0.0781 -0.64062,0 -0.90625,0.28125 -0.26562,0.26563 -0.26562,1.01563 v 0.76562 h 1.60937 v 1.09375 h -1.60937 v 7.20313 z m 4.11719,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55468,0 v -8.29688 h 1.26563 v 1.17188 q 0.90625,-1.35938 2.64062,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.24219,3.375 q -1.17188,-1.4
 6875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path81"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path83"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path85"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path87"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path89"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path91"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path93"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 532.8373,210.97375 h 99.40155 v 27.46457 H 532.8373 Z"
+       id="path95"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 542.7123,232.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.14062,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 12.14685,-2.21875 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60
 938,1.4375 z m 5.7406,4.125 2.53125,-3.59375 -2.34375,-3.3125 h 1.46875 l 1.0625,1.60937 q 0.29688,0.46875 0.48438,0.78125 0.28125,-0.4375 0.51562,-0.76562 l 1.17188,-1.625 h 1.40625 l -2.39063,3.25 2.5625,3.65625 h -1.4375 l -1.42187,-2.14063 -0.375,-0.59375 -1.8125,2.73438 z m 10.00781,-1.04688 0.17188,1.03125 q -0.5,0.10938 -0.89063,0.10938 -0.64062,0 -1,-0.20313 -0.34375,-0.20312 -0.48437,-0.53125 -0.14063,-0.32812 -0.14063,-1.39062 v -3.96875 h -0.85937 v -0.90625 h 0.85937 v -1.71875 l 1.17188,-0.70313 v 2.42188 h 1.17187 v 0.90625 h -1.17187 v 4.04687 q 0,0.5 0.0469,0.64063 0.0625,0.14062 0.20313,0.23437 0.14062,0.0781 0.40625,0.0781 0.20312,0 0.51562,-0.0469 z m 0.0624,3.70313 v -0.85938 h 7.76563 v 0.85938 z m 8.4906,-2.65625 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.1406
 2,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188
  v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.36561,1.23438 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0.89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z m 6.0531,2.0625 1.15625,-0.1875 q 0.10937,0.70312 0.54687,1.07812 0.45313,0.35938 1.25,0.35938 0.8125,0 1.20
 313,-0.32813 0.39062,-0.32812 0.39062,-0.76562 0,-0.39063 -0.35937,-0.625 -0.23438,-0.15625 -1.1875,-0.39063 -1.29688,-0.32812 -1.79688,-0.5625 -0.48437,-0.25 -0.75,-0.65625 -0.25,-0.42187 -0.25,-0.9375 0,-0.45312 0.20313,-0.84375 0.21875,-0.40625 0.57812,-0.67187 0.28125,-0.1875 0.75,-0.32813 0.46875,-0.14062 1.01563,-0.14062 0.8125,0 1.42187,0.23437 0.60938,0.23438 0.90625,0.64063 0.29688,0.39062 0.40625,1.0625 l -1.14062,0.15625 q -0.0781,-0.53125 -0.45313,-0.82813 -0.375,-0.3125 -1.0625,-0.3125 -0.8125,0 -1.15625,0.26563 -0.34375,0.26562 -0.34375,0.625 0,0.23437 0.14063,0.42187 0.15625,0.1875 0.45312,0.3125 0.17188,0.0625 1.03125,0.29688 1.25,0.32812 1.73438,0.54687 0.5,0.20313 0.78125,0.60938 0.28125,0.40625 0.28125,1 0,0.59375 -0.34375,1.10937 -0.34375,0.51563 -1,0.79688 -0.64063,0.28125 -1.45313,0.28125 -1.34375,0 -2.04687,-0.5625 -0.70313,-0.5625 -0.90625,-1.65625 z m 7.16406,4.71875 v -12.20313 h 2.57812 v 0.96875 h -1.40625 v 10.25 h 1.40625 v 0.98438 z m 5.64044,0
  h -2.59375 v -0.98438 h 1.42188 v -10.25 h -1.42188 v -0.96875 h 2.59375 z"
+       id="path97"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789 z"
+       id="path99"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path101"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path103"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 445.60104,298.39108 h 99.40158 v 27.46457 h -99.40158 z"
+       id="path105"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 455.39792,318.91107 v -6.21875 h 0.9375 v 0.875 q 0.6875,-1.01563 1.98437,-1.01563 0.5625,0 1.03125,0.20313 0.48438,0.20312 0.71875,0.53125 0.23438,0.32812 0.32813,0.76562 0.0469,0.29688 0.0469,1.03125 v 3.82813 h -1.04687 v -3.78125 q 0,-0.65625 -0.125,-0.96875 -0.125,-0.3125 -0.4375,-0.5 -0.3125,-0.20313 -0.73438,-0.20313 -0.67187,0 -1.17187,0.4375 -0.48438,0.42188 -0.48438,1.60938 v 3.40625 z m 7.64258,0 h -0.98438 v -8.59375 h 1.0625 v 3.0625 q 0.67188,-0.82813 1.70313,-0.82813 0.57812,0 1.07812,0.23438 0.51563,0.21875 0.84375,0.64062 0.34375,0.42188 0.53125,1.01563 0.1875,0.59375 0.1875,1.26562 0,1.59375 -0.79687,2.46875 -0.79688,0.875 -1.89063,0.875 -1.10937,0 -1.73437,-0.92187 z m -0.0156,-3.15625 q 0,1.10937 0.3125,1.60937 0.5,0.8125 1.34375,0.8125 0.6875,0 1.1875,-0.59375 0.51563,-0.59375 0.51563,-1.79687 0,-1.21875 -0.48438,-1.79688 -0.48437,-0.57812 -1.17187,-0.57812 -0.6875,0 -1.20313,0.60937 -0.5,0.59375 -0.5,1.73438 z m 4.73633,5.54687 v -0.76562 h 
 7 v 0.76562 z m 11.9082,-4.39062 1.09375,0.125 q -0.25,0.95312 -0.95312,1.48437 -0.70313,0.53125 -1.78125,0.53125 -1.35938,0 -2.17188,-0.84375 -0.79687,-0.84375 -0.79687,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79687,0.84375 0.79687,2.39063 0,0.0937 0,0.28125 h -4.64062 q 0.0625,1.03125 0.57812,1.57812 0.51563,0.53125 1.29688,0.53125 0.57812,0 0.98437,-0.29687 0.42188,-0.3125 0.65625,-0.96875 z m -3.45312,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39063,-1.1875 -0.51562,-0.60937 -1.3125,-0.60937 -0.73437,0 -1.23437,0.48437 -0.48438,0.48438 -0.53125,1.3125 z m 9.9082,3.70313 v -0.78125 q -0.59375,0.92187 -1.73437,0.92187 -0.75,0 -1.375,-0.40625 -0.625,-0.42187 -0.96875,-1.15625 -0.34375,-0.73437 -0.34375,-1.6875 0,-0.92187 0.3125,-1.6875 0.3125,-0.76562 0.9375,-1.15625 0.625,-0.40625 1.39062,-0.40625 0.5625,0 1,0.23438 0.4375,0.23437 0.71875,0.60937 v -3.07812 h 1.04688 v 8.59375 z m -3.32812,-3.10938 q 0,1.20313 0.5,1.79688 0.5,0
 .57812 1.1875,0.57812 0.6875,0 1.17187,-0.5625 0.48438,-0.5625 0.48438,-1.71875 0,-1.28125 -0.5,-1.875 -0.48438,-0.59375 -1.20313,-0.59375 -0.70312,0 -1.17187,0.57813 -0.46875,0.5625 -0.46875,1.79687 z m 5.76758,3.625 1.03125,0.15625 q 0.0625,0.46875 0.35937,0.6875 0.39063,0.29688 1.0625,0.29688 0.73438,0 1.125,-0.29688 0.40625,-0.29687 0.54688,-0.8125 0.0937,-0.32812 0.0781,-1.35937 -0.6875,0.8125 -1.71875,0.8125 -1.28125,0 -1.98437,-0.92188 -0.70313,-0.9375 -0.70313,-2.21875 0,-0.89062 0.3125,-1.64062 0.32813,-0.76563 0.9375,-1.17188 0.60938,-0.40625 1.4375,-0.40625 1.10938,0 1.82813,0.89063 v -0.75 h 0.96875 v 5.375 q 0,1.45312 -0.29688,2.0625 -0.29687,0.60937 -0.9375,0.95312 -0.64062,0.35938 -1.57812,0.35938 -1.10938,0 -1.79688,-0.5 -0.6875,-0.5 -0.67187,-1.51563 z m 0.875,-3.73437 q 0,1.21875 0.48437,1.78125 0.48438,0.5625 1.21875,0.5625 0.73438,0 1.21875,-0.5625 0.5,-0.5625 0.5,-1.75 0,-1.14063 -0.51562,-1.71875 -0.5,-0.57813 -1.21875,-0.57813 -0.70313,0 -1.20313,0.578
 13 -0.48437,0.5625 -0.48437,1.6875 z m 10.25195,1.21875 1.09375,0.125 q -0.25,0.95312 -0.95313,1.48437 -0.70312,0.53125 -1.78125,0.53125 -1.35937,0 -2.17187,-0.84375 -0.79688,-0.84375 -0.79688,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79688,0.84375 0.79688,2.39063 0,0.0937 0,0.28125 h -4.64063 q 0.0625,1.03125 0.57813,1.57812 0.51562,0.53125 1.29687,0.53125 0.57813,0 0.98438,-0.29687 0.42187,-0.3125 0.65625,-0.96875 z m -3.45313,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39062,-1.1875 -0.51563,-0.60937 -1.3125,-0.60937 -0.73438,0 -1.23438,0.48437 -0.48437,0.48438 -0.53125,1.3125 z m 5.45508,1.84375 1.03125,-0.15625 q 0.0937,0.625 0.48438,0.95313 0.40625,0.32812 1.14062,0.32812 0.71875,0 1.0625,-0.28125 0.35938,-0.29687 0.35938,-0.70312 0,-0.35938 -0.3125,-0.5625 -0.21875,-0.14063 -1.07813,-0.35938 -1.15625,-0.29687 -1.60937,-0.5 -0.4375,-0.21875 -0.67188,-0.59375 -0.23437,-0.375 -0.23437,-0.84375 0,-0.40625 0.1875,-0.76562 0.1875,
 -0.35938 0.51562,-0.59375 0.25,-0.17188 0.67188,-0.29688 0.42187,-0.125 0.92187,-0.125 0.71875,0 1.26563,0.21875 0.5625,0.20313 0.82812,0.5625 0.26563,0.35938 0.35938,0.95313 l -1.03125,0.14062 q -0.0625,-0.46875 -0.40625,-0.73437 -0.32813,-0.28125 -0.95313,-0.28125 -0.71875,0 -1.03125,0.25 -0.3125,0.23437 -0.3125,0.5625 0,0.20312 0.125,0.35937 0.14063,0.17188 0.40625,0.28125 0.15625,0.0625 0.9375,0.26563 1.125,0.3125 1.5625,0.5 0.4375,0.1875 0.6875,0.54687 0.25,0.35938 0.25,0.90625 0,0.53125 -0.3125,1 -0.29687,0.45313 -0.875,0.71875 -0.57812,0.25 -1.3125,0.25 -1.21875,0 -1.85937,-0.5 -0.625,-0.51562 -0.79688,-1.5 z"
+       id="path107"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 89.115486,28.062992 H 289.55644 V 55.527557 H 89.115486 Z"
+       id="path109"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 98.084236,54.98299 5.125004,-13.359375 h 1.90625 l 5.46875,13.359375 h -2.01563 l -1.54687,-4.046875 h -5.59375 l -1.468754,4.046875 z m 3.859374,-5.484375 h 4.53125 l -1.40625,-3.703125 q -0.625,-1.6875 -0.9375,-2.765625 -0.26562,1.28125 -0.71875,2.546875 z m 9.84982,5.484375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07813,-1.59375 0.875,0 1.60937,0.3125 0.73438,0.3125 1.09375,0.828125 0.375,0.5 0.51563,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64063 v -5.890625 q 0,-1 -0.20312,-1.484375 -0.1875,-0.5 -0.67188,-0.796875 -0.48437,-0.296875 -1.14062,-0.296875 -1.04688,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 16.68821,-1.1875 q -0.92186,0.765625 -1.76561,1.09375 -0.82813,0.3125 -1.79688,0.3125 -1.59375,0 -2.45312,-0.78125 -0.85938,-0.78125 -0.85938,-1.984375 0,-0.71875 0.32813,-1.296875 0.32812,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45312,-0.25 1.98438,-0.234375 2.92187,-0.5625 0.0156,-
 0.34375 0.0156,-0.421875 0,-1 -0.46874,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70312,0.40625 -0.54688,0.40625 -0.8125,1.421875 l -1.60938,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45313,-0.984375 0.95312,-0.34375 2.1875,-0.34375 1.25,0 2.01561,0.296875 0.78125,0.28125 1.14063,0.734375 0.375,0.4375 0.51562,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10937,2.890625 0.10938,0.59375 0.40625,1.15625 h -1.70312 q -0.26563,-0.515625 -0.32813,-1.1875 z m -0.14062,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01562,0.140625 -1.4375,0.328125 -0.42187,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45313,0.4375 0.9375,0 1.67187,-0.40625 0.75,-0.421875 1.09374,-1.140625 0.26563,-0.5625 0.26563,-1.640625 z m 7.78197,3.390625 0.23437,1.453125 q -0.6875,0.140625 -1.23437,0.140625 -0.89063,0 -1.39063,-0.28125 -0.48437,-0.28125 -0.6875,-0.734375 -0.20312,-0.46875 -0.20312,-1.9375 V 46.57674
  h -1.20313 v -1.265625 h 1.20313 V 42.92049 l 1.625,-0.984375 v 3.375 h 1.65625 v 1.265625 h -1.65625 v 5.671875 q 0,0.6875 0.0781,0.890625 0.0937,0.203125 0.28125,0.328125 0.20313,0.109375 0.57813,0.109375 0.26562,0 0.71875,-0.0625 z m 0.9958,-3.375 q 0,-2.6875 1.48437,-3.96875 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.29759,4.84375 v -9.671875 h 1.46875 v 1.359375 q 0.45313,-0.71875 1.20313,-1.140625 0.76562,-0.4375 1.71875,-0.4375 1.07812,0 1.76562,0.453125 0.6875,0.4375 0.96875,1.234375 1.15625,-1.6875 2.98438,-1
 .6875 1.45312,0 2.21875,0.796875 0.78125,0.796875 0.78125,2.453125 v 6.640625 h -1.64063 v -6.09375 q 0,-0.984375 -0.15625,-1.40625 -0.15625,-0.4375 -0.57812,-0.703125 -0.42188,-0.265625 -0.98438,-0.265625 -1.01562,0 -1.6875,0.6875 -0.67187,0.671875 -0.67187,2.15625 v 5.625 h -1.64063 v -6.28125 q 0,-1.09375 -0.40625,-1.640625 -0.40625,-0.546875 -1.3125,-0.546875 -0.6875,0 -1.28125,0.359375 -0.59375,0.359375 -0.85937,1.0625 -0.25,0.703125 -0.25,2.03125 v 5.015625 z m 15.46268,3.71875 -0.1875,-1.53125 q 0.54687,0.140625 0.9375,0.140625 0.54687,0 0.875,-0.1875 0.32812,-0.171875 0.54687,-0.5 0.15625,-0.25 0.5,-1.21875 0.0469,-0.140625 0.14063,-0.40625 l -3.67188,-9.6875 h 1.76563 l 2.01562,5.59375 q 0.39063,1.078125 0.70313,2.25 0.28125,-1.125 0.67187,-2.203125 l 2.07813,-5.640625 h 1.64062 l -3.6875,9.828125 q -0.59375,1.609375 -0.92187,2.203125 -0.4375,0.8125 -1,1.1875 -0.5625,0.375 -1.34375,0.375 -0.48438,0 -1.0625,-0.203125 z m 13.98018,-8.5625 q 0,-2.6875 1.48437,-3.96875 
 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.68821,4.84375 v -8.40625 h -1.45312 v -1.265625 h 1.45312 v -1.03125 q 0,-0.96875 0.17188,-1.453125 0.23437,-0.640625 0.82812,-1.03125 0.59375,-0.390625 1.67188,-0.390625 0.6875,0 1.53125,0.15625 l -0.25,1.4375 q -0.5,-0.09375 -0.95313,-0.09375 -0.75,0 -1.0625,0.328125 -0.3125,0.3125 -0.3125,1.1875 v 0.890625 h 1.89063 v 1.265625 h -1.89063 v 8.40625 z m 16.28849,-1.1875 q -0.92188,0.765625 -1.76563,1.09375 -0.82812,0.3125 -1.79687,0.3125 -1.59375,0 -2.45313,-0.78125 -0.85937,
 -0.78125 -0.85937,-1.984375 0,-0.71875 0.32812,-1.296875 0.32813,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45313,-0.25 1.98437,-0.234375 2.92187,-0.5625 0.0156,-0.34375 0.0156,-0.421875 0,-1 -0.46875,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70313,0.40625 -0.54687,0.40625 -0.8125,1.421875 l -1.60937,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45312,-0.984375 0.95313,-0.34375 2.1875,-0.34375 1.25,0 2.01563,0.296875 0.78125,0.28125 1.14062,0.734375 0.375,0.4375 0.51563,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10938,2.890625 0.10937,0.59375 0.40625,1.15625 h -1.70313 q -0.26562,-0.515625 -0.32812,-1.1875 z m -0.14063,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01563,0.140625 -1.4375,0.328125 -0.42188,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45312,0.4375 0.9375,0 1.67188,-0.40625 0.75,-0.421875 1.09375,-1.140625 0.26562,-0.5625 0.26562
 ,-1.640625 z m 9.38715,4.859375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07812,-1.59375 0.875,0 1.60938,0.3125 0.73437,0.3125 1.09375,0.828125 0.375,0.5 0.51562,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64062 v -5.890625 q 0,-1 -0.20313,-1.484375 -0.1875,-0.5 -0.67187,-0.796875 -0.48438,-0.296875 -1.14063,-0.296875 -1.04687,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 9.76634,-4.84375 q 0,-2.6875 1.48438,-3.96875 1.25,-1.078125 3.04687,-1.078125 2,0 3.26563,1.3125 1.26562,1.296875 1.26562,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64062,1.65625 -1.0625,0.59375 -2.32813,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79688,2.796875 0.8125,0.921875 2.04687,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23437,0 -2.04687,0.921875 -0.79688,0.90625 -0.79688,2.765625 z m 15.56322,4.84375 v -1.2187
 5 q -0.90625,1.4375 -2.70313,1.4375 -1.15625,0 -2.125,-0.640625 -0.96875,-0.640625 -1.5,-1.78125 -0.53125,-1.140625 -0.53125,-2.625 0,-1.453125 0.48438,-2.625 0.48437,-1.1875 1.4375,-1.8125 0.96875,-0.625 2.17187,-0.625 0.875,0 1.54688,0.375 0.6875,0.359375 1.10937,0.953125 v -4.796875 h 1.64063 V 54.98299 Z m -5.17188,-4.828125 q 0,1.859375 0.78125,2.78125 0.78125,0.921875 1.84375,0.921875 1.07813,0 1.82813,-0.875 0.75,-0.890625 0.75,-2.6875 0,-1.984375 -0.76563,-2.90625 -0.76562,-0.9375 -1.89062,-0.9375 -1.07813,0 -1.8125,0.890625 -0.73438,0.890625 -0.73438,2.8125 z m 15.90697,1.71875 1.6875,0.203125 q -0.40625,1.484375 -1.48438,2.3125 -1.07812,0.8125 -2.76562,0.8125 -2.125,0 -3.375,-1.296875 -1.23438,-1.3125 -1.23438,-3.671875 0,-2.453125 1.25,-3.796875 1.26563,-1.34375 3.26563,-1.34375 1.9375,0 3.15625,1.328125 1.23437,1.3125 1.23437,3.703125 0,0.15625 0,0.4375 h -7.21875 q 0.0937,1.59375 0.90625,2.453125 0.8125,0.84375 2.01563,0.84375 0.90625,0 1.54687,-0.46875 0.64063,
 -0.484375 1.01563,-1.515625 z m -5.39063,-2.65625 h 5.40625 q -0.10937,-1.21875 -0.625,-1.828125 -0.78125,-0.953125 -2.03125,-0.953125 -1.125,0 -1.90625,0.765625 -0.76562,0.75 -0.84375,2.015625 z"
+       id="path111"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path113"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path115"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path117"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path119"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path121"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path123"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path125"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path127"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path129"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path131"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path133"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path135"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path137"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path139"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path141"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path143"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path145"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path147"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,107.32317 h 14.62836 v 5.59806 H 850.9376 Z"
+       id="path149"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path151"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path153"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path155"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,125.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path157"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path159"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path161"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path163"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path165"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path167"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path169"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path171"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path173"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path175"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path177"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path179"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path181"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path183"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path185"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path187"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path189"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path191"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path193"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path195"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path197"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path199"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path201"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path203"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path205"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path207"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path209"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path211"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path213"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,211.32317 h 14.62836 v 5.59807 H 850.9376 Z"
+       id="path215"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path217"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path219"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path221"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,229.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path223"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path225"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path227"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path229"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path231"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path233"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path235"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path237"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path239"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path241"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path243"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path245"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path247"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path249"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path251"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path253"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path255"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path257"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path259"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path261"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path263"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path265"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path267"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path269"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path271"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path273"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path275"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path277"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path279"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,307.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path281"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path283"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path285"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path287"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,325.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path289"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path291"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path293"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path295"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path297"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path299"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path301"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path303"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path305"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path307"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path309"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path311"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path313"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path315"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path317"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path319"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path321"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path323"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path325"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path327"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path329"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path331"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path333"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path335"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path337"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path339"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path341"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path343"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path345"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,403.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path347"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path349"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path351"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path353"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,421.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path355"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path357"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path359"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path361"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path363"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path365"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path367"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path369"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path371"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path373"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path375"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90552,-71.85828 20.47638,-35.92914 40.95276,-71.85827 81.90552,-71.85827"
+       id="path377"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90546,-71.85826 10.23822,-17.96457 20.47638,-35.92915 33.27411,-49.40257 6.39886,-6.73671 13.43762,-12.35065 21.43622,-16.2804 3.99933,-1.96487 8.23858,-3.5087 12.75781,-4.56131 2.25958,-0.52631 4.58911,-0.92981 6.99371,-1.20174 1.20227,-0.13596 2.42334,-0.23902 3.66376,-0.3081 l 0.35388,-0.0172"
+       id="path379"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1456,117.62166 -1.0957,1.15275 3.06024,-1.20262 -3.11731,-1.04582 z"
+       id="path381"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74017,-33.62204 C 722.18577,242.81102 742.87083,226 784.24088,226"
+       id="path383"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74011,-33.62204 10.34253,-8.4055 20.68506,-16.81102 33.61322,-23.11514 6.46405,-3.15207 13.57452,-5.7788 21.6546,-7.61751 4.0401,-0.91934 8.32251,-1.64169 12.88776,-2.1342 2.28265,-0.24626 4.63592,-0.43506 7.06506,-0.5623 1.21448,-0.0636 2.448,-0.11184 3.70105,-0.14415 l 0.39166,-0.009"
+       id="path385"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 780.814,226.03992 -1.11139,1.1376 3.07648,-1.16049 -3.10266,-1.08851 z"
+       id="path387"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 19.86224,3.52755 39.72443,7.05511 79.44885,7.05511"
+       id="path389"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 9.93109,1.76376 19.86218,3.52755 32.27606,4.85037 6.20697,0.66144 13.03455,1.21261 20.79322,1.59842 3.87939,0.19293 7.99151,0.34451 12.37512,0.44784 2.19183,0.0517 4.45147,0.0913 6.78399,0.11798 1.1662,0.0133 2.35065,0.0235 3.55384,0.0303 l 0.2395,9.7e-4"
+       id="path391"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 774.23114,347.34512 -1.12762,1.12155 3.09283,-1.11627 -3.08673,-1.1329 z"
+       id="path393"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90552,22.1575 20.47638,11.07874 40.95276,22.15747 81.90552,22.15747"
+       id="path395"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90546,22.1575 10.23822,5.53937 20.47638,11.07874 33.27411,15.23328 6.39886,2.07724 13.43762,3.80829 21.43622,5.02005 3.99933,0.60583 8.23858,1.08188 12.75781,1.40649 2.25958,0.16226 4.58911,0.28668 6.99371,0.37052 1.20227,0.0419 2.42334,0.0737 3.66376,0.095 l 0.35291,0.005"
+       id="path397"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1446,441.53223 -1.1333,1.11575 3.09845,-1.10037 -3.08087,-1.14874 z"
+       id="path399"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 780.8373,58.973755 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path401"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="M 811.0304,80.77375 V 73.8675 h 1.0625 v 0.984375 q 0.75,-1.140625 2.1875,-1.140625 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.359375 0.375,0.859375 0.0625,0.328125 0.0625,1.140625 v 4.25 h -1.17188 v -4.203125 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.359375 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.796875 v 3.78125 z m 6.97498,-3.453125 q 0,-1.921875 1.07812,-2.84375 0.89063,-0.765625 2.17188,-0.765625 1.42187,0 2.32812,0.9375 0.90625,0.921875 0.90625,2.578125 0,1.328125 -0.40625,2.09375 -0.39062,0.765625 -1.15625,1.1875 -0.76562,0.421875 -1.67187,0.421875 -1.45313,0 -2.35938,-0.921875 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.328125 0.57813,1.984375 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.671875 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.984375 z m 
 11.13123,3.453125 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.453125 -0.6875,-0.453125 -1.07812,-1.265625 -0.375,-0.828125 -0.375,-1.890625 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.453125 1.54687,-0.453125 0.625,0 1.10938,0.265625 0.5,0.25 0.79687,0.671875 v -3.421875 h 1.17188 v 9.546875 z m -3.70313,-3.453125 q 0,1.328125 0.5625,1.984375 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.421875 -0.54687,-2.078125 Q 829.2616,74.68 828.46473,74.68 q -0.78125,0 -1.3125,0.640625 -0.51563,0.625 -0.51563,2 z m 11.3656,1.234375 1.20313,0.140625 q -0.28125,1.0625 -1.0625,1.65625 Q 837.3772,80.93 836.17408,80.93 q -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.609375 0,-1.75 0.89063,-2.703125 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.109375 0,0.3125 h -5.15625 q 0.0625,1.140625 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1
 .10937,-0.328125 0.45313,-0.34375 0.71875,-1.078125 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.859375 -0.4375,-1.296875 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.546875 -0.54688,0.53125 -0.60938,1.4375 z m 9.89667,-0.578125 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.6875,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.296875 0.95313,0.890625 0.375,0.578125 0.59375,1.421875 0.21875,0.828125 0.21875,2.25 0,1.671875 -0.35938,2.703125 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.984375 -0.95313,-1.1875 -0.95313,-3.875 z m 1.20313,0 q 0,2.34375 0.54687,3.125 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.125 0,-2.359375 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path403"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,162.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path405"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,184.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 h -1.17188 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.14062 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path407"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,258.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path409"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,280.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29688 q -0.0156,-0.42188 0.14063,-0.8125 0.23437,-0.64063 0.76562,-1.26563 0.53125,-0.625 1.53125,-1.45312 1.5625,-1.26563 2.10938,-2.01563 0.54687,-0.75 0.54687,-1.40625 0,-0.70312 -0.5,-1.17187 -0.5,-0.48438 -1.29687,-0.48438 -0.85938,0 -1.375,0.51563 -0.5,0.5 -0.5,1.39062 l -1.20313,-0.10937 q 0.125,-1.35938 0.92188,-2.0625 0.8125,-0.70313 2.17187,-0.70313 1.375,0 2.17188,0.76563 0.8125,0.75 0.8125,1.875 0,0.57812 -0.23438,1.14062 -0.23437,0.54688 -0.78125,1.15625 -0.54687,0.60938 -1.8125,1.67188 -1.04687,0.89062 -1.35937,1.21875 -0.29688,0.3125 -0.48438,0.625 z"
+       id="path411"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,354.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path413"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,376.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z"
+       id="path415"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/graph_mem_layout.svg b/doc/guides/prog_guide/img/graph_mem_layout.svg
new file mode 100644
index 000000000..1d41729c9
--- /dev/null
+++ b/doc/guides/prog_guide/img/graph_mem_layout.svg
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg428"
+   sodipodi:docname="Graph_mem_layout.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata434">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs432" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview430"
+     showgrid="false"
+     inkscape:zoom="3.4037037"
+     inkscape:cx="505.84248"
+     inkscape:cy="270.46053"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g426" />
+  <clipPath
+     id="g81c521b992_0_3.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path223" />
+  </clipPath>
+  <g
+     clip-path="url(#g81c521b992_0_3.0)"
+     id="g426">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path226" />
+    <path
+       fill="#ffffff"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path228" />
+    <path
+       stroke="#741b47"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path230" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m143.0105 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path232" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m271.45407 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path234" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 100.52126l129.44095 0"
+       fill-rule="nonzero"
+       id="path236" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 151.71811l129.44095 0"
+       fill-rule="nonzero"
+       id="path238" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 204.91496l129.44095 0"
+       fill-rule="nonzero"
+       id="path240" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 240.11182l129.44095 0"
+       fill-rule="nonzero"
+       id="path242" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 291.30865l129.44095 0"
+       fill-rule="nonzero"
+       id="path244" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 342.50552l129.44095 0"
+       fill-rule="nonzero"
+       id="path246" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 393.70236l129.44095 0"
+       fill-rule="nonzero"
+       id="path248" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 444.8992l129.44095 0"
+       fill-rule="nonzero"
+       id="path250" />
+    <path
+       fill="#000000"
+       d="m172.35416 118.58688l0 -1.125l4.03125 -0.015625l0 3.546875q-0.921875 0.75 -1.921875 1.125q-0.984375 0.359375 -2.03125 0.359375q-1.40625 0 -2.5625 -0.59375q-1.140625 -0.609375 -1.734375 -1.734375q-0.578125 -1.140625 -0.578125 -2.546875q0 -1.40625 0.578125 -2.609375q0.59375 -1.203125 1.6875 -1.78125q1.09375 -0.59375 2.515625 -0.59375q1.03125 0 1.859375 0.34375q0.84375 0.328125 1.3125 0.9375q0.484375 0.59375 0.734375 1.546875l-1.140625 0.3125q-0.21875 -0.71875 -0.53125 -1.140625q-0.3125 -0.421875 -0.90625 -0.671875q-0.59375 -0.25 -1.3125 -0.25q-0.875 0 -1.515625 0.265625q-0.625 0.265625 -1.015625 0.703125q-0.375 0.421875 -0.59375 0.9375q-0.359375 0.875 -0.359375 1.921875q0 1.265625 0.4375 2.125q0.4375 0.859375 1.265625 1.28125q0.84375 0.421875 1.796875 0.421875q0.8125 0 1.59375 -0.3125q0.78125 -0.328125 1.1875 -0.6875l0 -1.765625l-2.796875 0zm5.726425 3.734375l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.593
 75 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q
 -0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.7031
 25 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm11.302963 0l0 -9.546875l1.265625 0l0 3.921875l4.953125 0l0 -3.921875l1.265625 0l0 9.546875l-1.265625 0l0 -4.5l-4.953125 0l0 4.5l-1.265625 0zm14.172028 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.718
 75 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.3
 59375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm7.4749756 3.46875l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234
 375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path252" />
+    <path
+       fill="#000000"
+       d="m161.10791 135.96188l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.631226 -2.03125l1.1875 0.109375q0 0.40625 0.109375 0.609375q0.109375 0.203125 0.34375 0.3125q0.328125 0.140625
  0.828125 0.140625q1.0625 0 1.53125 -0.546875q0.3125 -0.375 0.578125 -1.625l0.109375 -0.5625q-0.921875 0.9375 -1.953125 0.9375q-1.046875 0 -1.75 -0.765625q-0.703125 -0.78125 -0.703125 -2.1875q0 -1.171875 0.546875 -2.140625q0.5625 -0.984375 1.328125 -1.46875q0.765625 -0.5 1.578125 -0.5q1.359375 0 2.09375 1.28125l0.234375 -1.125l1.078125 0l-1.390625 6.671875q-0.21875 1.09375 -0.59375 1.703125q-0.375 0.625 -1.03125 0.953125q-0.65625 0.34375 -1.53125 0.34375q-0.828125 0 -1.4375 -0.21875q-0.59375 -0.203125 -0.890625 -0.625q-0.296875 -0.40625 -0.296875 -0.953125q0 -0.15625 0.03125 -0.34375zm1.46875 -3.6875q0 0.71875 0.140625 1.078125q0.203125 0.5 0.5625 0.765625q0.359375 0.25 0.796875 0.25q0.578125 0 1.140625 -0.40625q0.578125 -0.40625 0.9375 -1.25q0.359375 -0.859375 0.359375 -1.625q0 -0.859375 -0.484375 -1.359375q-0.46875 -0.515625 -1.15625 -0.515625q-0.4375 0 -0.84375 0.234375q-0.390625 0.234375 -0.75 0.703125q-0.34375 0.46875 -0.53125 1.140625q-0.171875 0.65625 -0.171875 0.9843
 75zm6.0062256 3.0625l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.110245 -0.859375q-0.625 0.53125 -1.1875 0.78125q-0.5625 0.234375 -1.203125 0.234375q-0.96875 0 -1.5625 -0.5625q-0.578125 -0.5625 -0.578125 -1.4375q0 -0.578125 0.265625 -1.015625q0.265625 -0.453125 0.703125 -0.71875q0.4375 -0.28125 1.0625 -0.390625q0.40625 -0.078125 1.515625 -0.125q1.109375 -0.046875 1.59375 -0.234375q0.125 -0.484375 0.125 -0.8125q0 -0.40625 -0.296875 -0.640625q-0.40625 -0.328125 -1.1875 -0.328125q-0.75 0 -1.21875 0.328125q-0.46875 0.328125 -0.6875 0.9375l-1.1875 -0.109375q0.359375 -1.015625 1.140625 -1.5625q0.796875 -0.546875 2.0 -0.546875q1.28125 0 2.03125 0.609375q0.578125 0.453125 0.578125 1.1875q0 0.546875 -0.15625 1.28125l-0.390625 1.71875q-0.1875 0.8
 125 -0.1875 1.328125q0 0.328125 0.15625 0.9375l-1.203125 0q-0.09375 -0.34375 -0.125 -0.859375zm0.421875 -2.640625q-0.234375 0.09375 -0.53125 0.15625q-0.28125 0.046875 -0.9375 0.109375q-1.03125 0.078125 -1.453125 0.21875q-0.421875 0.140625 -0.640625 0.453125q-0.21875 0.296875 -0.21875 0.671875q0 0.5 0.34375 0.828125q0.34375 0.3125 0.984375 0.3125q0.578125 0 1.109375 -0.3125q0.546875 -0.3125 0.859375 -0.859375q0.3125 -0.5625 0.484375 -1.578125zm1.7406006 6.15625l2.0 -9.5625l1.09375 0l-0.203125 0.953125q0.59375 -0.625 1.078125 -0.859375q0.484375 -0.25 1.015625 -0.25q0.984375 0 1.625 0.71875q0.65625 0.71875 0.65625 2.0625q0 1.078125 -0.359375 1.96875q-0.34375 0.875 -0.875 1.421875q-0.515625 0.546875 -1.046875 0.796875q-0.53125 0.25 -1.09375 0.25q-1.25 0 -1.921875 -1.265625l-0.78125 3.765625l-1.1875 0zm2.328125 -5.484375q0 0.78125 0.109375 1.078125q0.171875 0.421875 0.53125 0.6875q0.375 0.25 0.875 0.25q1.015625 0 1.640625 -1.140625q0.625 -1.140625 0.625 -2.328125q0 -0.875 -0.4218
 75 -1.359375q-0.421875 -0.484375 -1.046875 -0.484375q-0.453125 0 -0.84375 0.25q-0.375 0.234375 -0.703125 0.703125q-0.328125 0.46875 -0.546875 1.15625q-0.21875 0.6875 -0.21875 1.1875zm5.6624756 2.828125l2.0 -9.546875l1.171875 0l-0.765625 3.671875q0.65625 -0.640625 1.21875 -0.90625q0.578125 -0.28125 1.171875 -0.28125q0.859375 0 1.328125 0.453125q0.484375 0.453125 0.484375 1.1875q0 0.359375 -0.203125 1.34375l-0.859375 4.078125l-1.171875 0l0.875 -4.1875q0.1875 -0.90625 0.1875 -1.140625q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.671875 -0.21875q-0.640625 0 -1.21875 0.34375q-0.578125 0.328125 -0.90625 0.90625q-0.328125 0.578125 -0.609375 1.875l-0.609375 2.96875l-1.1875 0z"
+       fill-rule="nonzero"
+       id="path254" />
+    <path
+       fill="#000000"
+       d="m189.80461 173.51811l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875
  -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.968
 75 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path256" />
+    <path
+       fill="#000000"
+       d="m160.76096 182.86186q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125
  -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.93988 -1.703125q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0625 0.046875 -0.125 0.046875l-2.359375 0l-1.46875 7.296875q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.26
 5625 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 -0.015625 -0.109375l1.46875 -7.296875l-2.359375 0q-0.09375 0 -0.125 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.09375 0.046875 -0.171875q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.078125 0.09375 -0.109375q0.046875 -0.046875 0.109375 -0.046875l5.859375 0q0.078125 0 0.109375 0.0625q0.03125 0.0625 0.03125 0.171875zm5.838608 -0.015625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0468
 75 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm5.4453735 9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625
  -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm9.278656 -9.234375q0 0.046875 -0.015625 0.109375q-0.015625 0.0625 -0.03125 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.078125 0.109375q-0.03125 0.03125 -0.078125 0.03125q-0.09375 0 -0.265625 -0.109375q-0.171875 -0.125 -0.453125 -0.265625q-0.265625 -0.15625 -0.671875 -0.265625q-0.390625 -0.125 -0.9375 -0.125q-0.578125 0 -1.09375 0.171875q-0.5 0.171875 -0.921875 0.484375q-0.421875 0.296875 -0.75 0.703125q-0.328125 0.40625 -0.5625 0.90625q-0.234375 0.484375 -0.359375 1.015625q-0.109375 0.53125 -0.109375 1.078125q0 0.5625 0.15625 1.015625q0.171875 0.4375 0.484375 0.734375q0.3125 0.296875 0.75 0.453125q0.4375 0.15625 0.984375 0.15625q0.40625 0 0.84375 -0.09375q0.453125 -0.09375 0.828125 -0.296875l0.484375 -2.4375l-1.953125 0q-0.078125 0 -0.125 -0.046875q-0.03125 -0.0625 -
 0.03125 -0.171875q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.078125 0.046875 -0.15625q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.0625 0.09375 -0.09375q0.046875 -0.046875 0.109375 -0.046875l2.671875 0q0.1875 0 0.265625 0.125q0.078125 0.125 0.03125 0.328125l-0.640625 3.25q-0.03125 0.125 -0.078125 0.203125q-0.03125 0.0625 -0.09375 0.125q-0.046875 0.0625 -0.296875 0.171875q-0.234375 0.109375 -0.59375 0.234375q-0.359375 0.109375 -0.8125 0.1875q-0.453125 0.09375 -0.953125 0.09375q-0.84375 0 -1.484375 -0.203125q-0.640625 -0.21875 -1.078125 -0.640625q-0.4375 -0.421875 -0.671875 -1.015625q-0.21875 -0.59375 -0.21875 -1.328125q0 -0.703125 0.15625 -1.375q0.15625 -0.671875 0.453125 -1.28125q0.3125 -0.609375 0.75 -1.109375q0.4375 -0.515625 1.0 -0.890625q0.578125 -0.375 1.25 -0.578125q0.6875 -0.21875 1.484375 -0.21875q0.453125 0 0.84375 0.078125q0.40625 0.078125 0.71875 0.203125q0.3125 0.109375 0.515625 0.25q0.21875 0.125 0.296875 0.203125q0.078125 0.0625 0
 .109375 0.140625q0.03125 0.0625 0.03125 0.15625zm6.9862976 0.84375q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0
  -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.424255 6.09375q0.03125 0.140625 0.015625 0.234375q-0.015625 0.078125 -0.078125 0.125q-0.046875 0.046875 -0.1875 0.046875q-0.125 0.015625 -0.34375 0.015625q-0.140625 0 -0.25 -0.015625q-0.109375 0 -0.171875 -0.015625q-0.046875 -0.03125 -0.078125 -0.0625q-0.015625 -0.046875 -0.03125 -0.09375l-0.3125 -2.0625l-3.5 0l-1.09375 2.03125q-0.046875 0.078125 -0.09375 0.
 125q-0.03125 0.03125 -0.109375 0.0625q-0.078125 0.015625 -0.203125 0.015625q-0.109375 0.015625 -0.28125 0.015625q-0.203125 0 -0.3125 -0.015625q-0.125 -0.015625 -0.15625 -0.046875q-0.046875 -0.046875 -0.03125 -0.140625q0.015625 -0.09375 0.09375 -0.234375l4.375 -7.8125q0.046875 -0.078125 0.09375 -0.125q0.0625 -0.046875 0.140625 -0.0625q0.09375 -0.03125 0.21875 -0.03125q0.125 -0.015625 0.3125 -0.015625q0.21875 0 0.34375 0.015625q0.140625 0 0.21875 0.03125q0.09375 0.015625 0.125 0.0625q0.03125 0.046875 0.046875 0.125l1.25 7.828125zm-2.1875 -6.90625l0 0l-2.28125 4.1875l2.921875 0l-0.640625 -4.1875zm9.930588 0.859375q0 0.34375 -0.078125 0.71875q-0.078125 0.375 -0.265625 0.734375q-0.171875 0.359375 -0.4375 0.6875q-0.265625 0.328125 -0.65625 0.578125q-0.375 0.234375 -0.875 0.375q-0.5 0.140625 -1.1875 0.140625l-1.15625 0l-0.59375 3.046875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-
 0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.546875 -7.78125q0.046875 -0.265625 0.203125 -0.375q0.171875 -0.109375 0.390625 -0.109375l1.65625 0q0.328125 0 0.578125 0.03125q0.25 0.015625 0.484375 0.0625q0.359375 0.078125 0.640625 0.25q0.28125 0.15625 0.46875 0.40625q0.203125 0.234375 0.296875 0.546875q0.109375 0.3125 0.109375 0.6875zm-1.1875 0.109375q0 -0.40625 -0.203125 -0.6875q-0.1875 -0.296875 -0.609375 -0.40625q-0.15625 -0.046875 -0.34375 -0.0625q-0.1875 -0.015625 -0.40625 -0.015625l-1.046875 0l-0.671875 3.375l1.0625 0q0.46875 0 0.78125 -0.09375q0.328125 -0.109375 0.5625 -0.28125q0.25 -0.171875 0.40625 -0.390625q0.171875 -0.234375 0.265625 -0.46875q0.109375 -0.25 0.15625 -0.5q0.046875 -0.25 0.046875 -0.46875zm7.76033 6.171875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0
 .015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l0.734375 -3.75l-3.828125 0l-0.734375 3.75q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l1.609375 -8.109375q0.015625 -0.03125 0.046875 -0.0625q0.046875 -0.046875 0.109375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.265625 0.015625q0.109375 0.015625 0.15625 0.046875q0.0625 0.015625 0.078125 0.0625q0.015625 0.03125 0.015625 0.0625l-0.671875 3.390625l3.828125 0l0.671875 -3.390625q0.015625 -0.03125 0.046875 -0.0625q0.03125 -0.046875 0.09375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.28125 -0.015625q0.15625 0 0.25 0.015625q0.109375 0.015625 0.171875 0.046875q0.0625 
 0.015625 0.078125 0.0625q0.015625 0.03125 0 0.0625l-1.609375 8.109375zm7.3821716 1.890625q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625 -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm7.497406 -9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.03125 0.078125 -0.078125 0.15625q-0.03125 0.0625 -0.078125 0.109375q-0.046875 0.046875 -0.125 0.046875l-3.078125 0l-0.5625 2.859375l2.90625 0q0.078125 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.140625q0 0.0625 -0.015625 0.140625q0 0.0625 -0.015625 0.140625q-
 0.015625 0.0625 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.90625 0l-0.703125 3.5q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0.015625q-0.109375 -0.015625 -0.171875 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.109375l1.5625 -7.796875q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l3.78125 0q0.09375 0 0.125 0.0625q0.03125 0.0625 0.03125 0.15625zm6.3270416 0q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.07812
 5 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm6.7109985 7.734375q-0.015625 0.140625 -0.078125 0.234375q-0.0625 0.078125 -0.140625 0.140625q-0.0625 0.0625 -0.15625 0.09375q-0.078125 0.015625 -0.171875 0.015625l-0.421875 0q-0.171875 0 -0.296875 -0.03125q-0.109375 -0.046875 -0.203125 -0.140625q-0.078125 -0.09375 -0.15625 -0.234375q-0.0625 -0
 .15625 -0.140625 -0.390625l-1.578125 -4.484375q-0.15625 -0.484375 -0.328125 -0.953125q-0.15625 -0.484375 -0.296875 -0.96875l-0.015625 0q-0.078125 0.53125 -0.1875 1.0625q-0.09375 0.515625 -0.203125 1.046875l-0.96875 4.90625q-0.015625 0.0625 -0.0625 0.109375q-0.03125 0.03125 -0.09375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.140625 0 -0.25 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.0625 -0.046875q-0.015625 -0.046875 0 -0.109375l1.546875 -7.765625q0.046875 -0.265625 0.21875 -0.375q0.171875 -0.109375 0.34375 -0.109375l0.5 0q0.15625 0 0.265625 0.03125q0.125 0.03125 0.203125 0.125q0.09375 0.078125 0.15625 0.21875q0.078125 0.125 0.15625 0.328125l1.59375 4.5625q0.140625 0.421875 0.28125 0.84375q0.15625 0.421875 0.296875 0.84375l0.015625 0q0.09375 -0.53125 0.203125 -1.09375q0.109375 -0.578125 0.203125 -1.109375l0.921875 -4.5625q0.015625 -0.0625 0.046875 -0.09375q0.03125 -0.03125 0.09375 -0.0625q0.0625 -0.03125 0.15
 625 -0.03125q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.25 0.015625q0.109375 0 0.15625 0.03125q0.0625 0.03125 0.078125 0.0625q0.015625 0.03125 0.015625 0.09375l-1.5625 7.765625zm9.090393 -7.09375q0 0.140625 -0.046875 0.34375q-0.046875 0.203125 -0.125 0.328125q-0.078125 0.109375 -0.15625 0.109375q-0.09375 0 -0.21875 -0.125q-0.125 -0.125 -0.328125 -0.265625q-0.203125 -0.140625 -0.53125 -0.25q-0.328125 -0.125 -0.828125 -0.125q-0.546875 0 -1.0 0.203125q-0.4375 0.203125 -0.8125 0.5625q-0.359375 0.34375 -0.640625 0.796875q-0.265625 0.453125 -0.453125 0.953125q-0.171875 0.5 -0.265625 1.015625q-0.078125 0.5 -0.078125 0.953125q0 0.515625 0.125 0.921875q0.125 0.40625 0.375 0.6875q0.25 0.28125 0.609375 0.421875q0.359375 0.140625 0.8125 0.140625q0.515625 0 0.875 -0.109375q0.375 -0.109375 0.625 -0.25q0.265625 -0.140625 0.4375 -0.25q0.1875 -0.125 0.296875 -0.125q0.078125 0 0.109375 0.0625q0.03125 0.046875 0.03125 0.15625q0 0.03125 -0.015625 0.09375q-0.015625 0.0625 -0.03125 0.14062
 5q0 0.0625 -0.015625 0.15625q-0.015625 0.078125 -0.046875 0.15625q-0.03125 0.0625 -0.0625 0.125q-0.015625 0.0625 -0.09375 0.125q-0.0625 0.0625 -0.28125 0.203125q-0.21875 0.125 -0.53125 0.234375q-0.3125 0.109375 -0.71875 0.1875q-0.390625 0.09375 -0.828125 0.09375q-0.671875 0 -1.203125 -0.203125q-0.53125 -0.203125 -0.90625 -0.578125q-0.375 -0.390625 -0.578125 -0.96875q-0.203125 -0.578125 -0.203125 -1.328125q0 -0.609375 0.125 -1.265625q0.140625 -0.65625 0.390625 -1.265625q0.25 -0.625 0.625 -1.171875q0.390625 -0.546875 0.890625 -0.953125q0.5 -0.421875 1.125 -0.65625q0.640625 -0.25 1.390625 -0.25q0.484375 0 0.890625 0.109375q0.421875 0.109375 0.703125 0.28125q0.296875 0.15625 0.421875 0.28125q0.140625 0.125 0.140625 0.296875zm6.2602844 -0.640625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.07
 8125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.60935974 3.0l3.3437347 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.0468597 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.4999847 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875z"
+       fill-rule="nonzero"
+       id="path258" />
+    <path
+       fill="#000000"
+       d="m172.59329 223.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859
 375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.250732 0l0 -9.546875l3.59375 0q1.09375 0 1.75 0.296875q0.65625 0.28125 1.03125 0.890625q0.375 0.609375 0.375 1.265625q0 0.609375 -0.34375 1.15625q-0.328125 0.53125 -0.984375 0.859375q0.859375 0.25 1.328125 0.875q0.46875 0.609375 0.
 46875 1.4375q0 0.671875 -0.296875 1.25q-0.28125 0.578125 -0.703125 0.890625q-0.40625 0.3125 -1.03125 0.46875q-0.625 0.15625 -1.546875 0.15625l-3.640625 0zm1.265625 -5.53125l2.0625 0q0.84375 0 1.203125 -0.109375q0.484375 -0.140625 0.71875 -0.46875q0.25 -0.34375 0.25 -0.84375q0 -0.46875 -0.234375 -0.828125q-0.21875 -0.359375 -0.640625 -0.5q-0.421875 -0.140625 -1.453125 -0.140625l-1.90625 0l0 2.890625zm0 4.40625l2.375 0q0.609375 0 0.859375 -0.046875q0.4375 -0.078125 0.734375 -0.25q0.296875 -0.1875 0.484375 -0.53125q0.1875 -0.359375 0.1875 -0.8125q0 -0.53125 -0.28125 -0.921875q-0.265625 -0.40625 -0.75 -0.5625q-0.484375 -0.15625 -1.40625 -0.15625l-2.203125 0l0 3.28125zm12.06163 1.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 
 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm3.1624756 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm8.156113 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625
  0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path260" />
+    <path
+       fill="#000000"
+       d="m186.7589 261.9118l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896713 -0.5781
 25q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875z"
+       fill-rule="nonzero"
+       id="path262" />
+    <path
+       fill="#000000"
+       d="m163.32709 275.55243l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path264" />
+    <path
+       fill="#000000"
+       d="m186.7589 313.10867l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 
 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318588 4.125
 l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125z"
+       fill-rule="nonzero"
+       id="path266" />
+    <path
+       fill="#000000"
+       d="m163.32709 326.7493l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.42187
 5 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125 
 0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -
 1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125
 q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125 
 1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.0156
 25 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-
 0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path268" />
+    <path
+       fill="#000000"
+       d="m186.7589 364.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052963 3.0l0 
 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0z"
+       fill-rule="nonzero"
+       id="path270" />
+    <path
+       fill="#000000"
+       d="m163.32709 377.94614l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path272" />
+    <path
+       fill="#000000"
+       d="m185.65256 415.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625
  0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365463 4.12
 5l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0z"
+       fill-rule="nonzero"
+       id="path274" />
+    <path
+       fill="#000000"
+       d="m163.32709 429.14297l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path276" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m92.06693 62.29134l230.33072 0l0 27.464565l-230.33072 0z"
+       fill-rule="evenodd"
+       id="path278" />
+    <path
+       fill="#000000"
+       d="m114.3782 84.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0164948 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.8748627 -1.171875l1.2031174 0.140625q-0.28125 1.0625 -1.0624924 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375
  -0.96875q1.390625 0 2.265625 0.9375q0.8749924 0.9375 0.8749924 2.65625q0 0.109375 0 0.3125l-5.1562424 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.443718 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.271851 -2.078125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125 2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.
 390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm6.6312256 3.578125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -
 0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.45
 3125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm10.677963 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -
 0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.17984 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.43
 75zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875zm4.8434753 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836807 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0
 .9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 
 1.453125l0 3.578125l-1.171875 0zm10.664932 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071198 2.65625l-0.125 -1.09375q0.375 0.109375 0.65
 625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625
 q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875
  0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1
 .234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path280"
+       style="fill:#c8ab37" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925z"
+       fill-rule="evenodd"
+       id="path282" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path284" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path286" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m64.91338 302.8189l99.40157 0l0 27.46457l-99.40157 0z"
+       fill-rule="evenodd"
+       id="path288" />
+    <path
+       fill="#000000"
+       d="m74.71026 323.3389l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.
 609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm7.658203 -2.390625l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.283203 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625
  -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm10.017578 3.109375l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm10.220703 1.109375l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2
 .390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -
 0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path290" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m687.0105 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path292" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m815.45404 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path294" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 100.52126l129.44092 0"
+       fill-rule="nonzero"
+       id="path296" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 135.71811l129.44092 0"
+       fill-rule="nonzero"
+       id="path298" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 172.91496l129.44092 0"
+       fill-rule="nonzero"
+       id="path300" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 208.11182l129.44092 0"
+       fill-rule="nonzero"
+       id="path302" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 243.30865l129.44092 0"
+       fill-rule="nonzero"
+       id="path304" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 278.50552l129.44092 0"
+       fill-rule="nonzero"
+       id="path306" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 313.70236l129.44092 0"
+       fill-rule="nonzero"
+       id="path308" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 348.8992l129.44092 0"
+       fill-rule="nonzero"
+       id="path310" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 384.09607l129.44092 0"
+       fill-rule="nonzero"
+       id="path312" />
+    <path
+       fill="#000000"
+       d="m733.8046 122.32126l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 
 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.9687
 5 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path314" />
+    <path
+       fill="#000000"
+       d="m709.2222 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm9.155334 3.0625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm2.5392456 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.9281006 3.453125l-2.125 -6.90625l1.21875 0l1.09375 3.984375l0.421875 1.484375q0.015625 -0.109375 0.359375 -1.421875l1.09375 -4.046875l1.203125 0l1.03125 4.0l0.34375 1.328125l0.40
 625 -1.34375l1.171875 -3.984375l1.140625 0l-2.15625 6.90625l-1.21875 0l-1.09375 -4.140625l-0.265625 -1.171875l-1.40625 5.3125l-1.21875 0zm8.343872 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.60
 9375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.031
 25q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6311035 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.6
 09375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0
 625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q
 -1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.
 53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path316" />
+    <path
+       fill="#000000"
+       d="m710.1765 191.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.234497 -0.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375
  -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6468506 3.453125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm9.974976 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.
 171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874878 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-
 0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.
 296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.2031
 25q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.87
 5 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625z"
+       fill-rule="nonzero"
+       id="path318" />
+    <path
+       fill="#000000"
+       d="m711.206 229.9118l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.438232 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-
 0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.5218506 1.40625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.
 265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 3.703125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125
  0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875
 q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0
 .140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6310425 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.187
 5q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.188416 -2.21875l1.203125 0.140625q-0.281
 25 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125
  0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path320" />
+    <path
+       fill="#000000"
+       d="m706.683 265.10867l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.14
 0625l-0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765
 625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1
 .90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.8967285 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.56251526 1.71875 -0.56251526q0.78125 0 1.359375 0.3125q0.578125 0.29689026 0.953125 0.89064026q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.
 21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm10.2404785 7.359375l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.
 671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path322" />
+    <path
+       fill="#000000"
+       d="m706.683 300.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.375
  -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42
 1875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.3186035 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm7.0217285 2.65625l0 -9.5625l1.07812
 5 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.0
 78125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path324" />
+    <path
+       fill="#000000"
+       d="m706.683 335.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.37
 5 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 
 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4
 21875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052979 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375
  -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm5.2873535 3.78125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.7
 5 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path326" />
+    <path
+       fill="#000000"
+       d="m705.57666 370.69922l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218872 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.
 375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843506 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.42187
 5 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1
 .421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm13.1875 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875
  0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.062
 5 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path328" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m636.06696 70.291336l230.33069 0l0 27.46457l-230.33069 0z"
+       fill-rule="evenodd"
+       id="path330" />
+    <path
+       fill="#000000"
+       d="m660.59735 92.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0165405 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874817 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.
 96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.4437256 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.490601 -2.65625l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.9
 0625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.07
 8125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.7873535 0.671875q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0
 .921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359
 375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.179871 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q
 0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.26
 5625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.07812
 5zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.5
 78125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4
 375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.4923096 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0
 .375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 
 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.7
 03125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path332"
+       style="fill:#008033" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013z"
+       fill-rule="evenodd"
+       id="path334" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path336" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path338" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m821.979 278.50656l99.40155 0l0 27.46457l-99.40155 0z"
+       fill-rule="evenodd"
+       id="path340" />
+    <path
+       fill="#000000"
+       d="m831.7759 299.02655l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0
 .609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm11.908203 -4.390625l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.908203 3.703125l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q
 0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm5.767578 3.625l1.03125 0.15625q0.0625 0.46875 0.359375 0.6875q0.390625 0.296875 1.0625 0.296875q0.734375 0 1.125 -0.296875q0.40625 -0.296875 0.546875 -0.8125q0.09375 -0.328125 0.078125 -1.359375q-0.6875 0.8125 -1.71875 0.8125q-1.28125 0 -1.984375 -0.921875q-0.703125 -0.9375 -0.703125 -2.21875q0 -0.890625 0.3125 -1.640625q0.328125 -0.765625 0.9375 -1.171875q0.609375 -0.40625 1.4375 -0.40625q1.109375 0 1.828125 0.890625l0 -0.75l0.96875 0l0 5.375q0 1.453125 -0.296875 2.0625q-0.296875 0.609375 -0.9375 0.953125q-0.640625 0.359375 -1.578125 0.359375q-1.109375 0 -1.796875 -0.5q-0.6875 -0.5 -0.671875 -1.515625zm0.875 -3.734375q0 1.21875 0.484375 1.7
 8125q0.484375 0.5625 1.21875 0.5625q0.734375 0 1.21875 -0.5625q0.5 -0.5625 0.5 -1.75q0 -1.140625 -0.515625 -1.71875q-0.5 -0.578125 -1.21875 -0.578125q-0.703125 0 -1.203125 0.578125q-0.484375 0.5625 -0.484375 1.6875zm10.251953 1.21875l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.35937
 5 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path342" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m359.4252 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path344" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m520.34906 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path346" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 100.52126l161.92126 0"
+       fill-rule="nonzero"
+       id="path348" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 135.71811l161.92126 0"
+       fill-rule="nonzero"
+       id="path350" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 170.91496l161.92126 0"
+       fill-rule="nonzero"
+       id="path352" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 206.11182l161.92126 0"
+       fill-rule="nonzero"
+       id="path354" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 241.30865l161.92126 0"
+       fill-rule="nonzero"
+       id="path356" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 276.50552l161.92126 0"
+       fill-rule="nonzero"
+       id="path358" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 311.70236l161.92126 0"
+       fill-rule="nonzero"
+       id="path360" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 346.8992l161.92126 0"
+       fill-rule="nonzero"
+       id="path362" />
+    <path
+       fill="#000000"
+       d="m379.84702 119.25876l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.1937256 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359
 375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.802948 1.25q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.4531
 25l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-
 0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.906
 25 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path364" />
+    <path
+       fill="#000000"
+       d="m379.84702 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.615601 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm6.584198 -3.453125q0 -1.921875 1.07812
 5 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0
 .125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.3437
 5 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.17
 1875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path366" />
+    <path
+       fill="#000000"
+       d="m376.8892 189.65247l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921
 875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.
 546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.
 328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42187
 5 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.7500305 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921
 875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0
 .484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03
 125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203
 125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path368" />
+    <path
+       fill="#000000"
+       d="m372.88116 227.9118l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896698 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.8029785 1.2
 5q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0
 .734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.
 34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm
 9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path370" />
+    <path
+       fill="#000000"
+       d="m372.88116 263.10867l0 -9.54689l6.90625 0l0 1.125l-5.640625 0l0 2.9218903l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5000153l0 7.5000153l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.5
 78125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.87
 5 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318573 4.125l-1.171875 0l0 -7.4687653q-0.421875 0.40626526 -1.109375 0.81251526q-0.6875 0.40625 -1.234375 0.609375l0 -1.1406403q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.57814zm6.5842285 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.6562
 5q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.
 359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.
 15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.7187653l1.171875 -0.703125l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path372" />
+    <path
+       fill="#000000"
+       d="m372.88116 298.3055l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052948 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm4.8498535 -2.328
 125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 
 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-
 0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375
 zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path374" />
+    <path
+       fill="#000000"
+       d="m371.7748 333.50235l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.7178955 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.75 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65
 625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625
  -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0
 .9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path376" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.77692 65.05774l280.09448 0l0 27.46457l-280.09448 0z"
+       fill-rule="evenodd"
+       id="path378" />
+    <path
+       fill="#000000"
+       d="m345.39603 83.51399l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.8593
 75 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.969635 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 0l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578
 125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836792 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0
 .8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.6649475 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.42187
 5q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-
 2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875
  0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.9218
 75 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.1093
 75q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.1247253 1.046875l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9842224 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.4531
 25 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 
 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm11.084351 1.203125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.521881 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.187
 5 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.896851 0l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm6.6468506 -4.734
 375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.9454346 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm7.1937256 0.578125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125
  2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm9.8811035 1.515625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.
 078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.
 34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375
  -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875
  -1.171875l0 -0.421875zm2.9906006 3.46875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.633667 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.2
 5 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625z"
+       fill-rule="nonzero"
+       id="path380"
+       style="fill:#88aa00" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m516.2336 178.6483l115.4646 0l0 27.464554l-115.4646 0z"
+       fill-rule="evenodd"
+       id="path382" />
+    <path
+       fill="#000000"
+       d="m532.2961 196.15266l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm5.9960938 -1.859375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.062
 5 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5zm8.71875 0.921875l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm5.0996094 0.171875q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.
 265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.35937
 5 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125z"
+       fill-rule="nonzero"
+       id="path384" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m589.60895 206.11285l-61.259888 0"
+       fill-rule="evenodd"
+       id="path386" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m589.60895 206.11285l-55.259888 0"
+       fill-rule="evenodd"
+       id="path388"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m534.34906 204.46114l-4.538086 1.6517181l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path390" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m520.7349 322.6483l115.46454 0l0 27.46457l-115.46454 0z"
+       fill-rule="evenodd"
+       id="path392" />
+    <path
+       fill="#000000"
+       d="m536.7974 340.15268l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm10.667969 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.876953 3.703125l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0
 .765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.705078 0l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875z"
+       fill-rule="nonzero"
+       id="path394" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m586.3438 346.90027l-61.259827 0"
+       fill-rule="evenodd"
+       id="path396" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m586.3438 346.90027l-55.259827 0"
+       fill-rule="evenodd"
+       id="path398"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m531.084 345.24854l-4.538086 1.6517334l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path400" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.45407 204.69554l85.51181 -102.645676"
+       fill-rule="evenodd"
+       id="path402" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.45407 204.69554l81.67139 -98.03577"
+       fill-rule="evenodd"
+       id="path404" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m354.39453 107.716995l1.6356201 -4.5439224l-4.1737366 2.4294815z"
+       fill-rule="evenodd"
+       id="path406" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 240.03412l88.0 105.60629"
+       fill-rule="evenodd"
+       id="path408" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 240.03412l84.15903 100.99686"
+       fill-rule="evenodd"
+       id="path410" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m353.94522 342.08835l4.1740417 2.4289856l-1.6362 -4.5437317z"
+       fill-rule="evenodd"
+       id="path412" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 392.81104l411.87402 -290.77167"
+       fill-rule="evenodd"
+       id="path414" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 392.81104l406.9724 -287.31128"
+       fill-rule="evenodd"
+       id="path416" />
+    <path
+       fill="#274e13"
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m678.98016 106.84912l2.7546997 -3.9666214l-4.659912 1.2679062z"
+       fill-rule="evenodd"
+       id="path418" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 442.09448l415.9685 -61.102356"
+       fill-rule="evenodd"
+       id="path420" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 442.09448l410.03223 -60.230347"
+       fill-rule="evenodd"
+       id="path422" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m681.3274 383.49832l4.249878 -2.2937317l-4.7299805 -0.9746704z"
+       fill-rule="evenodd"
+       id="path424" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/link_the_nodes.svg b/doc/guides/prog_guide/img/link_the_nodes.svg
new file mode 100644
index 000000000..4a127e67c
--- /dev/null
+++ b/doc/guides/prog_guide/img/link_the_nodes.svg
@@ -0,0 +1,3330 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg1003"
+   sodipodi:docname="Link_the_nodes.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata1009">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs1007" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview1005"
+     showgrid="false"
+     inkscape:zoom="1.2361274"
+     inkscape:cx="1097.0658"
+     inkscape:cy="171.4074"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1003" />
+  <clipPath
+     id="g7c2ed6c54d_0_300.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path2" />
+  </clipPath>
+  <g
+     clip-path="url(#g7c2ed6c54d_0_300.0)"
+     id="g1001">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path5" />
+    <path
+       fill="#ffffff"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path7" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path9" />
+    <path
+       fill="#ffffff"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path11" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path13" />
+    <path
+       fill="#fdf8f8"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path15" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path17" />
+    <path
+       fill="#d9ead3"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path19" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path21" />
+    <path
+       fill="#cfe2f3"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path23" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path25" />
+    <path
+       fill="#f4cccc"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path27" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path29" />
+    <path
+       fill="#f4cccc"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path31" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path33" />
+    <path
+       fill="#eeeeee"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path35" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path37" />
+    <path
+       fill="#eeeeee"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path39" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path41" />
+    <path
+       fill="#eeeeee"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path43" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path45" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m199.20312 266.6246l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path47" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482z"
+       fill-rule="evenodd"
+       id="path49" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path51" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path53" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m191.56721 277.2064l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path55" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path57" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path59" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path61" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path63" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path65" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path67" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path69" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path71" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path73" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path75" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m145.16678 229.68527l73.13281 0l0 16.320053l-73.13281 0z"
+       fill-rule="evenodd"
+       id="path77" />
+    <path
+       fill="#000000"
+       d="m164.66463 247.24533l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827484 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.54685974 -0.375 -0.85935974 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.82810974 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.9531097 -2.75q0 1.046875 0.43748474 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.42185974 0.5 -0.42185974 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.5
 15625 1.40625q0.46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.78125q0.25 -0.40625 0.6875 -0.65625q0.4375 -0.25 0.984375 -0.25q0.609375 0 1.0 0.265625q0.390625 0.25 0.5625 0.703125q0.65625 -0.96875 1.703125 -0.96875q0.828125 0 1.265625 0.46875q0.4375 0.453125 0.4375 1.390625l0 3.796875l-0.921875 0l0 -3.484375q0 -0.5625 -0.09375 -0.796875q-0.09375 -0.25 -0.34375 -0.40625q-0.234375 -0.15625 -0.546875 -0.15625q-0.59375 0 -0.984375 0.390625q-0.375 0.390625 -0.375 1.25l0 3.203125l-0.9375 0l0 -3.59375q0 -0.625 -0.234375 -0.9375q-0.21875 -0.3125 -0.75 -0.3125q-0.390625 0 -0.734375 0.21875q-0.328125 0.203125 -0.484375 0.609375q-0.140625 0.390625 -0.140625 1.15625l0 2.859375l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path79" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path81" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path83" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path85" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path87" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path89" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path91" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path93" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path95" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path97" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path99" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path101" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path103" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path105" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path107" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path109" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path111" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path113" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path115" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path117" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path119" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 186.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path121" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path123" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path125" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path127" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 197.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path129" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path131" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path133" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path135" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path137" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path139" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path141" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path143" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path145" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path147" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path149" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 149.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path151" />
+    <path
+       fill="#000000"
+       d="m286.14026 167.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path153" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path155" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path157" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path159" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path161" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path163" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path165" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path167" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path169" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path171" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path173" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path175" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path177" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path179" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path181" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path183" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path185" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path187" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path189" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path191" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path193" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 346.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path195" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path197" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path199" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path201" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 357.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path203" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path205" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path207" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path209" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path211" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path213" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path215" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path217" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path219" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path221" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path223" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 309.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path225" />
+    <path
+       fill="#000000"
+       d="m286.14026 327.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.840271 0.53125q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.46875 1.578125z"
+       fill-rule="nonzero"
+       id="path227" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path229" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path231" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path233" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path235" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path237" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path239" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path241" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path243" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path245" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path247" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path249" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path251" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path253" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path255" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path257" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path259" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path261" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path263" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path265" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path267" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 442.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path269" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path271" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path273" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path275" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 453.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path277" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path279" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path281" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path283" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path285" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path287" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path289" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path291" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path293" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path295" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path297" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 405.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path299" />
+    <path
+       fill="#000000"
+       d="m406.43945 423.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.809021 1.640625l0.921875 -0.140625q0.078125 0.5625 0.4375 0.859375q0.359375 0.296875 1.0 0.296875q0.640625 0 0.953125 -0.265625q0.3125 -0.265625 0.3125 -0.625q0 -0.3125 -0.28125 -0.5q-0.1875 -0.125 -0.953125 -0.3125q-1.03125 -0.265625 -1.4375 -0.453125q-0.390625 -0.1875 -0.59375 -0.515625q-0.203125 -0.34375 -0.203125 -0.75q0 -0.359375 0.171875 -0.671875q0.171875 -0.328125 0.453125 -0.53125q0.21875 -0.15625 0.59375 -0.265625q0.390625 -0.125 0.8125 -0.125q0.65625 0 1.140625 0.1875q0.5 0.1875 0.734375 0.515625q0.234375 0.3125 0.3125 0.859375l-0.90625 0.125q-0.0625 -0.4375 -0.375 -0.671875q-0.296875 -0.234375 -0.828125 -0.234375q-0.65625 0 -0.9375 0.21875q-0.265625 0.203125 -0.265625 0.484375q0 0.1875 0.109375 0.328125
 q0.125 0.15625 0.359375 0.25q0.140625 0.0625 0.828125 0.25q1.0 0.265625 1.390625 0.4375q0.390625 0.15625 0.609375 0.484375q0.234375 0.3125 0.234375 0.796875q0 0.46875 -0.28125 0.890625q-0.265625 0.40625 -0.78125 0.640625q-0.515625 0.21875 -1.171875 0.21875q-1.078125 0 -1.640625 -0.4375q-0.5625 -0.453125 -0.71875 -1.34375z"
+       fill-rule="nonzero"
+       id="path301" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path303" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path305" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path307" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path309" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path311" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path313" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path315" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path317" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path319" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path321" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path323" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path325" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path327" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path329" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path331" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path333" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path335" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path337" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path339" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path341" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 314.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path343" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path345" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path347" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path349" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 325.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path351" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path353" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path355" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path357" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path359" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path361" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path363" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path365" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path367" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path369" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path371" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 277.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path373" />
+    <path
+       fill="#000000"
+       d="m407.32922 295.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.582733 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.168396 3.296875l0 -5.53125l0.84375 0l0 0.84375q0.328125 -0.59375 0.59375 -0.78125q0.28125 -0.1875 0.609375 -0.1875q0.46875 0 0.953125 0.3125l-0.3125 0.859375q-0.34375 -0.203125 -0.6875 -0.203125q-0.3125 0 -0.5625 0.1875q-0.234375 0.1875 -0.34375 0.515625q-0.15625 0.5 -0.15625 1.09375l0 2.890625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path375" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path377" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path379" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path381" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path383" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path385" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path387" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path389" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path391" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path393" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path395" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path397" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path399" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path401" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path403" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path405" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path407" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path409" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path411" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path413" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path415" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 210.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path417" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path419" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path421" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path423" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 221.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path425" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path427" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path429" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path431" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path433" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path435" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path437" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path439" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path441" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path443" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path445" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 173.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path447" />
+    <path
+       fill="#000000"
+       d="m414.14026 191.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.699646 5.421875l0 -2.71875q-0.21875 0.3125 -0.609375 0.515625q-0.390625 0.203125 -0.828125 0.203125q-0.984375 0 -1.703125 -0.78125q-0.703125 -0.796875 -0.703125 -2.15625q0 -0.828125 0.28125 -1.484375q0.296875 -0.671875 0.84375 -1.015625q0.546875 -0.34375 1.203125 -0.34375q1.03125 0 1.609375 0.875l0 -0.75l0.84375 0l0 7.65625l-0.9375 0zm-2.875 -4.90625q0 1.0625 0.4375 1.609375q0.453125 0.53125 1.078125 0.53125q0.59375 0 1.015625 -0.5q0.4375 -0.515625 0.4375 -1.546875q0 -1.109375 -0.453125 -1.65625q-0.453125 -0.5625 -1.0625 -0.5625q-0.609375 0 -1.03125 0.515625q-0.421875 0.515625 -0.421875 1.609375z"
+       fill-rule="nonzero"
+       id="path449" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path451" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path453" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path455" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path457" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path459" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path461" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path463" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path465" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path467" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path469" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path471" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path473" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path475" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path477" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path479" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path481" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path483" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path485" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path487" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path489" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 106.62759l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path491" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path493" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path495" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path497" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path499" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path501" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path503" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path505" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path507" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path509" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path511" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path513" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path515" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path517" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path519" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path521" />
+    <path
+       fill="#000000"
+       d="m414.14026 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 5.421875l0 -7.65625l0.859375 0l0 0.71875q0.296875 -0.421875 0.671875 -0.625q0.390625 -0.21875 0.921875 -0.21875q0.703125 0 1.25 0.375q0.546875 0.359375 0.8125 1.03125q0.28125 0.65625 0.28125 1.453125q0 0.84375 -0.3125 1.53125q-0.296875 0.671875 -0.875 1.03125q-0.578125 0.359375 -1.21875 0.359375q-0.46875 0 -0.84375 -0.1875q-0.375 -0.203125 -0.609375 -0.515625l0 2.703125l-0.9375 0zm0.84375 -4.859375q0 1.0625 0.4375 1.578125q0.4375 0.515625 1.046875 0.515625q0.625 0 1.0625 -0.53125q0.453125 -0.53125 0.453125 -1.640625q0 -1.046875 -0.4375 -1.578125q-0.4375 -0.53125 -1.046875 -0.53125q-0.59375 0 -1.0625 0.5625q-0.453125 0.5625 -0.453125 1.625z"
+       fill-rule="nonzero"
+       id="path523" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path525" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path527" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path529" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path531" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path533" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path535" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path537" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path539" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path541" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path543" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path545" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path547" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path549" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path551" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path553" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path555" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path557" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path559" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path561" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path563" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path565" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path567" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path569" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path571" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path573" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path575" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path577" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path579" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path581" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path583" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path585" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path587" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path589" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path591" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path593" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path595" />
+    <path
+       fill="#000000"
+       d="m566.14026 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.809021 3.296875l0 -0.8125q-0.65625 0.9375 -1.75 0.9375q-0.5 0 -0.921875 -0.1875q-0.421875 -0.1875 -0.625 -0.46875q-0.203125 -0.28125 -0.296875 -0.703125q-0.046875 -0.265625 -0.046875 -0.875l0 -3.421875l0.9375 0l0 3.0625q0 0.734375 0.046875 1.0q0.09375 0.359375 0.375 0.578125q0.296875 0.203125 0.703125 0.203125q0.421875 0 0.796875 -0.203125q0.375 -0.21875 0.515625 -0.59375q0.15625 -0.375 0.15625 -1.078125l0 -2.96875l0.9375 0l0 5.53125l-0.828125 0z"
+       fill-rule="nonzero"
+       id="path597" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path599" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path601" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path603" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path605" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path607" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path609" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path611" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path613" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path615" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path617" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path619" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path621" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path623" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path625" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path627" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path629" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path631" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path633" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path635" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path637" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 298.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path639" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path641" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path643" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path645" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 309.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path647" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path649" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path651" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path653" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path655" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path657" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path659" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path661" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path663" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path665" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path667" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 261.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path669" />
+    <path
+       fill="#000000"
+       d="m566.43945 279.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm9.715271 3.296875l-2.09375 -5.53125l0.984375 0l1.1875 3.3125q0.1875 0.53125 0.359375 1.109375q0.109375 -0.4375 0.34375 -1.046875l1.21875 -3.375l0.96875 0l-2.09375 5.53125l-0.875 0z"
+       fill-rule="nonzero"
+       id="path671" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path673" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path675" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path677" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path679" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path681" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path683" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path685" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path687" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path689" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path691" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path693" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path695" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path697" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path699" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path701" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path703" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path705" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path707" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path709" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path711" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 434.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path713" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path715" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path717" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path719" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 445.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path721" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path723" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path725" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path727" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path729" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path731" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path733" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path735" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path737" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path739" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path741" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 397.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path743" />
+    <path
+       fill="#000000"
+       d="m566.43945 415.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.559021 3.296875l2.015625 -2.875l-1.859375 -2.65625l1.171875 0l0.84375 1.296875q0.234375 0.375 0.390625 0.625q0.21875 -0.34375 0.40625 -0.609375l0.9375 -1.3125l1.125 0l-1.921875 2.609375l2.0625 2.921875l-1.15625 0l-1.125 -1.71875l-0.296875 -0.46875l-1.453125 2.1875l-1.140625 0z"
+       fill-rule="nonzero"
+       id="path745" />
+    <path
+       fill="#ffffff"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path747" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path749" />
+    <path
+       fill="#ffffff"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path751" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path753" />
+    <path
+       fill="#fdf8f8"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path755" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path757" />
+    <path
+       fill="#d9ead3"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path759" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path761" />
+    <path
+       fill="#cfe2f3"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path763" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path765" />
+    <path
+       fill="#f4cccc"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path767" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path769" />
+    <path
+       fill="#f4cccc"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path771" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path773" />
+    <path
+       fill="#eeeeee"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path775" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path777" />
+    <path
+       fill="#eeeeee"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path779" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path781" />
+    <path
+       fill="#eeeeee"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path783" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path785" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.16473 106.62759l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path787" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path789" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path791" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path793" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m583.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path795" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path797" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path799" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path801" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path803" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path805" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path807" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path809" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path811" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path813" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path815" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m537.1668 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path817" />
+    <path
+       fill="#000000"
+       d="m559.62317 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm10.230896 2.453125l0.125 0.828125q-0.390625 0.09375 -0.703125 0.09375q-0.5 0 -0.78125 -0.15625q-0.28125 -0.171875 -0.40625 -0.4375q-0.109375 -0.265625 -0.109375 -1.109375l0 -3.171875l-0.6875 0l0 -0.734375l0.6875 0l0 -1.359375l0.9375 -0.5625l0 1.921875l0.9375 0l0 0.734375l-0.9375 0l0 3.234375q0 0.390625 0.046875 0.515625q0.046875 0.109375 0.15625 0.1875q0.109375 0.0625 0.328125 0.0625q0.15625 0 0.40625 -0.046875z"
+       fill-rule="nonzero"
+       id="path819" />
+    <path
+       fill="#ffffff"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path821" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path823" />
+    <path
+       fill="#ffffff"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path825" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path827" />
+    <path
+       fill="#fdf8f8"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path829" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path831" />
+    <path
+       fill="#d9ead3"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path833" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path835" />
+    <path
+       fill="#cfe2f3"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path837" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path839" />
+    <path
+       fill="#f4cccc"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path841" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path843" />
+    <path
+       fill="#f4cccc"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path845" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path847" />
+    <path
+       fill="#eeeeee"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path849" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path851" />
+    <path
+       fill="#eeeeee"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path853" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path855" />
+    <path
+       fill="#eeeeee"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path857" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path859" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m783.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path861" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path863" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path865" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path867" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m775.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path869" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path871" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path873" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path875" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path877" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path879" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path881" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path883" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path885" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path887" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path889" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m729.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path891" />
+    <path
+       fill="#000000"
+       d="m750.43945 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.137146 5.421875l-0.09375 -0.875q0.296875 0.078125 0.53125 0.078125q0.3125 0 0.5 -0.109375q0.1875 -0.09375 0.3125 -0.28125q0.078125 -0.140625 0.28125 -0.703125q0.03125 -0.078125 0.078125 -0.21875l-2.09375 -5.546875l1.015625 0l1.140625 3.203125q0.234375 0.609375 0.40625 1.28125q0.15625 -0.640625 0.375 -1.265625l1.1875 -3.21875l0.9375 0l-2.109375 5.625q-0.328125 0.90625 -0.515625 1.25q-0.25 0.46875 -0.578125 0.6875q-0.3125 0.21875 -0.765625 0.21875q-0.265625 0 -0.609375 -0.125z"
+       fill-rule="nonzero"
+       id="path893" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m211.71075 275.29062c16.11023 0 24.165344 -13.937012 32.220474 -27.874023c8.055115 -13.937012 16.110214 -27.874008 32.220474 -27.874008"
+       fill-rule="evenodd"
+       id="path895" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m211.71075 275.29062c16.11023 0 24.16536 -13.937012 32.220474 -27.874008c4.0275574 -6.968506 8.055115 -13.937012 13.089554 -19.163391c2.5172424 -2.6131897 5.286194 -4.790848 8.432709 -6.315201c1.5732727 -0.7621918 3.2409363 -1.3610382 5.018738 -1.7693481c0.4444275 -0.10209656 0.89575195 -0.19224548 1.3542175 -0.27009583c0.22921753 -0.038909912 0.4602356 -0.07473755 0.6930847 -0.107437134l0.2121582 -0.028259277"
+       fill-rule="evenodd"
+       id="path897" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m272.7317 219.76288l-1.0499573 1.1945496l3.011078 -1.3208618l-3.1556702 -0.923645z"
+       fill-rule="evenodd"
+       id="path899" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m206.79628 284.1058c0 19.13388 14.29921 28.700806 28.598434 38.26773c14.29921 9.566925 28.59842 19.13385 28.59842 38.2677"
+       fill-rule="evenodd"
+       id="path901" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m206.79628 284.1058c0 19.13385 14.29921 28.700775 28.59842 38.26773c7.1496124 4.7834473 14.299225 9.566925 19.661423 15.546234c2.6810913 2.989685 4.915344 6.2783203 6.4793396 10.015381c0.7819824 1.8685608 1.3963928 3.8492126 1.8153076 5.9606323c0.20947266 1.0557556 0.37008667 2.144165 0.47827148 3.2676392l0.00491333 0.05441284"
+       fill-rule="evenodd"
+       id="path903" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m263.83395 357.21786l-1.1755981 -1.0711365l1.2668762 3.0341797l0.9798584 -3.1386719z"
+       fill-rule="evenodd"
+       id="path905" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m324.40216 194.87454c0 -41.086624 33.811005 -82.17323 67.62204 -82.17323"
+       fill-rule="evenodd"
+       id="path907" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m324.40216 194.87454c0 -20.543304 8.452759 -41.086624 21.131866 -56.494095c6.339569 -7.7037506 13.735748 -14.123528 21.660187 -18.617378c3.9622498 -2.2469177 8.056519 -4.0123596 12.216888 -5.216072c2.0801697 -0.6018524 4.17688 -1.0632782 6.281769 -1.3742294c0.5262451 -0.07774353 1.0529785 -0.14608002 1.5801392 -0.20485687c0.26358032 -0.029388428 0.5272217 -0.056381226 0.7909546 -0.080963135l0.53601074 -0.045051575"
+       fill-rule="evenodd"
+       id="path909" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.6 112.841896l-1.0775146 1.1697693l3.0410461 -1.2503891l-3.1333008 -0.9968872z"
+       fill-rule="evenodd"
+       id="path911" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.75735 207.661c36.834625 0 73.66928 40.34645 73.66928 80.69292"
+       fill-rule="evenodd"
+       id="path913" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.75735 207.66098c18.417297 0 36.834625 10.086624 50.647614 25.216537c6.906494 7.564972 12.661926 16.390747 16.690704 25.84694c2.0144043 4.728119 3.597168 9.613831 4.6762695 14.578339c0.5395813 2.4822388 0.9532471 4.984192 1.2320251 7.4959717c0.1394043 1.2559204 0.24505615 2.5142822 0.31588745 3.7738953l0.017486572 0.35531616"
+       fill-rule="evenodd"
+       id="path915" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m400.33734 284.92798l-1.1535034 -1.0949097l1.2047119 3.0594177l1.0437012 -3.1180115z"
+       fill-rule="evenodd"
+       id="path917" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m331.69748 203.27638c15.078735 0 22.618134 3.3543396 30.157501 6.708664c7.5393677 3.3543396 15.078735 6.708664 30.15747 6.708664"
+       fill-rule="evenodd"
+       id="path919" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m331.69748 203.2764c15.078766 0 22.618134 3.3543243 30.157501 6.7086487c3.7696838 1.6771698 7.5393677 3.3543396 12.251495 4.612213c2.3560486 0.62893677 4.947693 1.1530457 7.8927917 1.519928c1.4725037 0.18344116 3.0333862 0.32757568 4.6973267 0.42582703c0.41601562 0.024597168 0.83847046 0.046279907 1.2675476 0.06503296l0.62176514 0.024765015"
+       fill-rule="evenodd"
+       id="path921" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.5859 216.63281l-1.1443787 1.1044312l3.109253 -1.0695038l-3.069275 -1.179306z"
+       fill-rule="evenodd"
+       id="path923" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m323.7046 355.2753c36.17325 0 72.346466 -3.1653748 72.346466 -6.330719"
+       fill-rule="evenodd"
+       id="path925" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m323.7046 355.2753c18.08664 0 36.17325 -0.7913208 49.73819 -1.9783325c6.782501 -0.59350586 12.43457 -1.2859497 16.390991 -2.0278015c0.98913574 -0.18551636 1.8722534 -0.37402344 2.6405945 -0.5649414l0.5545349 -0.14355469"
+       fill-rule="evenodd"
+       id="path927" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m393.02893 350.56067l-0.46139526 1.5220032l2.1943665 -2.4487l-3.2549744 0.4653015z"
+       fill-rule="evenodd"
+       id="path929" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.78412 359.65433c0 44.519684 28.614166 89.0394 57.228333 89.0394"
+       fill-rule="evenodd"
+       id="path931" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.7841 359.65433c0 22.259827 7.1535645 44.519684 17.88388 61.21457c5.3651733 8.347443 11.624512 15.30365 18.330963 20.172974c3.3532104 2.4346619 6.818207 4.3476562 10.339111 5.6519165c1.760437 0.6521301 3.534851 1.1521301 5.316223 1.4890747c0.44540405 0.084228516 0.8911743 0.15826416 1.3372803 0.22195435l0.60028076 0.0786438"
+       fill-rule="evenodd"
+       id="path933" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m380.59183 448.4835l-1.1914368 1.0534973l3.1529236 -0.9329529l-3.0149841 -1.3119812z"
+       fill-rule="evenodd"
+       id="path935" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.3904 229.5441c22.905518 0 34.358246 -5.2125854 45.811005 -10.425186c11.452759 -5.2126007 22.905518 -10.425201 45.811035 -10.425201"
+       fill-rule="evenodd"
+       id="path937" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.3904 229.54411c22.905518 0 34.358276 -5.2126007 45.811035 -10.425201c5.7263794 -2.606308 11.452759 -5.2126007 18.610748 -7.167328c3.5789795 -0.97735596 7.515869 -1.7918243 11.989563 -2.3619537c2.2368774 -0.2850647 4.607971 -0.50904846 7.13562 -0.6617737c1.263794 -0.07633972 2.5668335 -0.13487244 3.9116821 -0.17433167l0.73657227 -0.018814087"
+       fill-rule="evenodd"
+       id="path939" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58563 208.7347l-1.111084 1.1379547l3.0761108 -1.1614532l-3.1029663 -1.0875549z"
+       fill-rule="evenodd"
+       id="path941" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905518 -39.425186 41.811005 -52.566925c20.905548 -13.141724 41.811066 -26.283463 41.811066 -52.566925"
+       fill-rule="evenodd"
+       id="path943" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905487 -39.4252 41.811005 -52.566925c10.452759 -6.570862 20.905548 -13.141739 28.745087 -21.355316c3.9197998 -4.1067963 7.1862793 -8.624268 9.472839 -13.757751c1.1432495 -2.566742 2.041504 -5.287491 2.6539917 -8.187912c0.30621338 -1.4502106 0.5410156 -2.945343 0.69921875 -4.4886017c0.03955078 -0.38581085 0.07434082 -0.7746353 0.10424805 -1.1665115l0.013000488 -0.18595123"
+       fill-rule="evenodd"
+       id="path945" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m535.90155 124.26837l1.0835571 1.1641159l-1.0132446 -3.1280365l-1.234436 3.0475311z"
+       fill-rule="evenodd"
+       id="path947" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m454.75735 231.661c22.314941 0 33.472443 20.960617 44.629913 41.92125c11.157471 20.960632 22.314941 41.921265 44.629944 41.921265"
+       fill-rule="evenodd"
+       id="path949" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m454.75735 231.66098c22.314941 0 33.472412 20.960632 44.629913 41.921265c5.5787354 10.480316 11.157471 20.960632 18.13092 28.820862c3.4866943 3.9301147 7.3220825 7.2052307 11.680481 9.497803c2.1791992 1.1462708 4.4891357 2.046936 6.95166 2.6610107c1.2312012 0.30703735 2.5006104 0.54241943 3.810852 0.7010803c0.16375732 0.019836426 0.32818604 0.03845215 0.49328613 0.055877686l0.13989258 0.013671875"
+       fill-rule="evenodd"
+       id="path951" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.59436 315.33255l-1.1792603 1.0671082l3.1420288 -0.9690857l-3.0298462 -1.2772827z"
+       fill-rule="evenodd"
+       id="path953" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.78412 455.65433c24.307068 0 36.4606 -3.7401428 48.614166 -7.480316c12.153534 -3.7401428 24.307098 -7.4802856 48.614166 -7.4802856"
+       fill-rule="evenodd"
+       id="path955" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m446.78412 455.65433c24.307098 0 36.460632 -3.7401733 48.614166 -7.480316c6.076782 -1.8700867 12.153564 -3.7401733 19.749542 -5.1427307c3.7979736 -0.7012634 7.975708 -1.285675 12.723206 -1.6947632c2.3737793 -0.20455933 4.8898926 -0.36523438 7.5722656 -0.47479248c1.3411255 -0.05480957 2.723816 -0.09680176 4.151062 -0.12512207l0.99108887 -0.017120361"
+       fill-rule="evenodd"
+       id="path957" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58545 440.71948l-1.1160889 1.1329956l3.0812378 -1.1477966l-3.0981445 -1.1012878z"
+       fill-rule="evenodd"
+       id="path959" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-68.0 10.0 -136.0 52.5 -136.0 104.99999"
+       fill-rule="evenodd"
+       id="path961" />
+    <path
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-34.0 5.0 -68.0 18.125 -93.5 36.562492c-12.75 9.21875 -23.375 19.765625 -30.8125 31.289062c-3.71875 5.7617188 -6.640625 11.767586 -8.6328125 17.973633c-0.99606323 3.1030273 -1.7597351 6.2561035 -2.274414 9.453735c-0.25732422 1.5988159 -0.45239258 3.2087708 -0.5831299 4.829178c-0.032684326 0.4051056 -0.061340332 0.81085205 -0.0859375 1.2172546l-0.013824463 0.24894714"
+       fill-rule="evenodd"
+       id="path963" />
+    <path
+       fill="#980000"
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m301.8311 146.25957l-1.0921936 -1.1560669l1.0363464 3.1204681l1.2119141 -3.0565796z"
+       fill-rule="evenodd"
+       id="path965" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m628.3904 213.5441c24.905518 0 37.358276 -1.2125854 49.811035 -2.4251862c12.452759 -1.2126007 24.905518 -2.4252014 49.811035 -2.4252014"
+       fill-rule="evenodd"
+       id="path967" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m628.39044 213.54411c24.905518 0 37.358215 -1.2126007 49.810974 -2.4252014c6.2263794 -0.606308 12.452759 -1.2126007 20.235779 -1.6673279c3.8914795 -0.22735596 8.172058 -0.41682434 13.036499 -0.54945374c2.432129 -0.0663147 5.010254 -0.11842346 7.758606 -0.15393066c1.3741455 -0.01777649 2.7908936 -0.031402588 4.253235 -0.04055786l1.0998535 -0.0051574707"
+       fill-rule="evenodd"
+       id="path969" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m724.5854 208.7025l-1.1217041 1.1274567l3.086914 -1.1324921l-3.0926514 -1.1166687z"
+       fill-rule="evenodd"
+       id="path971" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m622.7841 431.67007c0 -45.75589 36.58264 -68.63385 73.165344 -91.51181c36.582703 -22.87793 73.165344 -45.75589 73.165344 -91.511795"
+       fill-rule="evenodd"
+       id="path973" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m622.7841 431.67007c0 -45.75592 36.582703 -68.63385 73.165344 -91.51178c18.29132 -11.438995 36.582703 -22.87796 50.30121 -37.176697c6.859253 -7.149353 12.575256 -15.013641 16.576538 -23.950348c2.0005493 -4.4683533 3.5724487 -9.204803 4.644226 -14.254028c0.5359497 -2.5246277 0.94677734 -5.1274414 1.2236328 -7.814026c0.13842773 -1.3433075 0.24334717 -2.70755 0.3137207 -4.093445l0.035339355 -0.7969208"
+       fill-rule="evenodd"
+       id="path975" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m769.04407 252.07283l1.1011353 1.1475525l-1.0605469 -3.11232l-1.1881104 3.0658875z"
+       fill-rule="evenodd"
+       id="path977" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.7573 117.00678c36.88977 0 55.334656 13.614174 73.77954 27.22834c18.444885 13.6141815 36.88977 27.228348 73.77954 27.228348"
+       fill-rule="evenodd"
+       id="path979" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m598.7573 117.006775c36.88977 0 55.334656 13.6141815 73.77954 27.228348c9.222473 6.807083 18.444946 13.6141815 29.972961 18.719482c5.764038 2.552658 12.104431 4.679886 19.30951 6.16893c3.602478 0.7445221 7.4211426 1.3295135 11.492004 1.728363c2.0354614 0.19943237 4.13385 0.35231018 6.2998657 0.45535278c0.5415039 0.025772095 1.0872803 0.04840088 1.6373291 0.06788635c0.2750244 0.009719849 0.5510864 0.018676758 0.8282471 0.026824951l0.8128052 0.021453857"
+       fill-rule="evenodd"
+       id="path981" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m742.8896 171.42342l-1.1376343 1.1113739l3.1026611 -1.0884094l-3.076416 -1.160614z"
+       fill-rule="evenodd"
+       id="path983" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m451.69748 115.27639c21.078735 0 31.618134 -0.645668 42.1575 -1.2913437c10.539368 -0.645668 21.078735 -1.291336 42.15747 -1.291336"
+       fill-rule="evenodd"
+       id="path985" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m451.6975 115.27639c21.078735 0 31.618103 -0.645668 42.15747 -1.291336c5.269684 -0.32283783 10.539368 -0.645668 17.126495 -0.8877945c3.2935486 -0.12106323 6.9164734 -0.22195435 11.033356 -0.29257202c2.0584717 -0.035308838 4.2404785 -0.063056946 6.5665894 -0.081970215c1.1630249 -0.009460449 2.3620605 -0.016708374 3.5997314 -0.021591187l0.40423584 -7.9345703E-4"
+       fill-rule="evenodd"
+       id="path987" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m532.5854 112.70034l-1.1224365 1.1267548l3.0876465 -1.1305542l-3.09198 -1.1186066z"
+       fill-rule="evenodd"
+       id="path989" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m604.3975 309.543c67.82678 0 135.6535 -36.299225 135.6535 -72.59842"
+       fill-rule="evenodd"
+       id="path991" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m604.3975 309.543c33.91339 0 67.82678 -9.074799 93.26178 -22.687012c12.717529 -6.8060913 23.31543 -14.7465515 30.734009 -23.25418c3.7092896 -4.2538147 6.623657 -8.649414 8.610779 -13.115921c0.9935913 -2.233261 1.7553711 -4.4842224 2.2686157 -6.7440643c0.12835693 -0.56495667 0.2411499 -1.1304779 0.3381958 -1.6963959c0.04852295 -0.28297424 0.09307861 -0.5660553 0.13366699 -0.84921265c0.020263672 -0.14157104 0.039611816 -0.28315735 0.057922363 -0.42478943l0.049560547 -0.4055481"
+       fill-rule="evenodd"
+       id="path993" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m739.852 240.36588l1.0574341 1.1879883l-0.94329834 -3.1498566l-1.302124 3.0192566z"
+       fill-rule="evenodd"
+       id="path995" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m49.937008 5.086614l504.22046 0l0 27.464567l-504.22046 0z"
+       fill-rule="evenodd"
+       id="path997" />
+    <path
+       fill="#000000"
+       d="m60.296383 32.00661l0 -13.359373l1.78125 0l0 11.78125l6.562496 0l0 1.5781231l-8.343746 0zm10.250713 -11.468748l0 -1.890625l1.640625 0l0 1.890625l-1.640625 0zm0 11.468748l0 -9.671873l1.640625 0l0 9.671873l-1.640625 0zm4.144821 0l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm10.375717 0l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm18.089554 -1.4687481l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -
 1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051788 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.078125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000717 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.437
 5l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324654 5.765623l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm9.766342 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.296
 8731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm15.563217 4.843748l0 -1.2187481q-0.90625 1.4374981 -2.703125 1.4374981q-1.15625 0 -2.125 -0.6406231q-0.96875 -0.640625 -1.5 -1.78125q-0.53125 -1.140625 -0.53125 -2.625q0 -1.453125 0.484375 -2.625q0.484375 -1.1875 1.4375 -1.8125q0.96875 -0.625 2.171875 -0.625q0.875 0 1.546875 0.375q0.6875 0.359375 1.109375 0.953125l0 -4.796875l1.640625 0l0 13.359373l-1.53125 0zm-5.171875 -4.828123q0 1.859375 0.78125 2.78125q0.78125 0.921875 1.84375 0.921875q1.078125 0 1.828125 -0.875q0.75 -0.890625 0.75 -2.6875q0 -1.984375 -0.765625 -2.90625q-0.765625 -0.9375 -1.890625 -0.9375q-1.078125 0 -1.8125 0.890625q-0.734375 0.890625 -0.734375 2.8125zm15.906967 1.71875l1.6875 0.203125q-0.40625 1.484375 -1.
 484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm8.485092 2.875l1.625 -0.25q0.125 0.96875 0.75 1.5q0.625 0.515625 1.75 0.515625q1.125 0 1.671875 -0.453125q0.546875 -0.46875 0.546875 -1.09375q0 -0.546875 -0.484375 -0.875q-0.328125 -0.21875 -1.671875 -0.546875q-1.8125 -0.46875 -2.515625 -0.796875q-0.6875 -0.328125 -1.046875 -0.90625q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.640625 0.296875 -1.1875q0.296875 -0.5625 0.8125 -0.921875q0.375 -0.28125 1.03125 -0.46875q0.671
 875 -0.203125 1.421875 -0.203125q1.140625 0 2.0 0.328125q0.859375 0.328125 1.265625 0.890625q0.421875 0.5625 0.578125 1.5l-1.609375 0.21875q-0.109375 -0.75 -0.640625 -1.171875q-0.515625 -0.421875 -1.46875 -0.421875q-1.140625 0 -1.625 0.375q-0.46875 0.375 -0.46875 0.875q0 0.3125 0.1875 0.578125q0.203125 0.265625 0.640625 0.4375q0.234375 0.09375 1.4375 0.421875q1.75 0.453125 2.4375 0.75q0.6875 0.296875 1.078125 0.859375q0.390625 0.5625 0.390625 1.40625q0 0.828125 -0.484375 1.546875q-0.46875 0.71875 -1.375 1.125q-0.90625 0.3906231 -2.046875 0.3906231q-1.875 0 -2.875 -0.7812481q-0.984375 -0.78125 -1.25 -2.328125zm18.745804 1.421875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.2
 03125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm0.99580383 -3.375q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm20.793396 1.296875l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0
 .96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.0 3.546873l0 -9.671873l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.062498l-1.625 0zm12.853302 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.
 484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm15.453842 4.578125q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.421875q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.4
 0625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm7.781967 3.390625l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm8.230179 -1.640625l1.6874847 0.203125q-0.40625 1.484375 -1.4843597 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.6
 71875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.1562347 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.2187347 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm17.902756 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051941 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.07
 8125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000702 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324646 9.468748l0 -13.374998l1.484375 0l0 1
 .25q0.53125 -0.734375 1.1875 -1.09375q0.671875 -0.375 1.625 -0.375q1.234375 0 2.171875 0.640625q0.953125 0.625 1.4375 1.796875q0.484375 1.15625 0.484375 2.546875q0 1.484375 -0.53125 2.671875q-0.53125 1.1875 -1.546875 1.828125q-1.015625 0.6249981 -2.140625 0.6249981q-0.8125 0 -1.46875 -0.3437481q-0.65625 -0.34375 -1.0625 -0.875l0 4.703123l-1.640625 0zm1.484375 -8.484373q0 1.859375 0.75 2.765625q0.765625 0.890625 1.828125 0.890625q1.09375 0 1.875 -0.921875q0.78125 -0.9375 0.78125 -2.875q0 -1.84375 -0.765625 -2.765625q-0.75 -0.921875 -1.8125 -0.921875q-1.046875 0 -1.859375 0.984375q-0.796875 0.96875 -0.796875 2.84375zm15.203857 3.59375q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.42187
 5q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.40625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm10.516357 1.3125l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3
 .71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.015625 3.546873l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm15.953125 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.01562
 5 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm12.719482 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.179077 1.4687481l0 -8.406248l-1.453125 0l0 -1.265625l1.453125 0l0 -1.03125q0 -0.96875 0.171875 -1.453125q0.234375 -0.640625 0.828125 -1.03125q0.59375 -0.390625 1.671875 -0.390625q0.6875 0 1.53125 0.15625l-0.25 1.4375q-0.5 -0.09375 -0.953125 -0.09375q-0.75 0 -1.0625 0.328125q-0.3125 0.3125 -0.3125 1.1875l0 0.8906
 25l1.890625 0l0 1.265625l-1.890625 0l0 8.406248l-1.625 0zm4.7457886 0l0 -13.359373l1.640625 0l0 13.359373l-1.640625 0zm3.5823364 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm11.078857 4.843748l-2.96875 -9.671873l1.703125 0l1.53125 5.578125l0.578125 2.078125q0.046875 -0.15625 0.5 -2.0l1.546875 -5.65625l1.6875 0l1.4375 5.609375l0.484375 1.84375l0.5625 -1.859375l1.65625 -5.59375l1.59375 0l-3.03125 9.671873l-1.703125 0l-1.53125 -5.796873l-0.375 -1.640625l-1.953125 7.437498l-1.71
 875 0z"
+       fill-rule="nonzero"
+       id="path999" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index fb250abf5..a98b16f0e 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -57,6 +57,7 @@ Programmer's Guide
     metrics_lib
     bpf_lib
     ipsec_lib
+    graph_lib
     source_org
     dev_kit_build_system
     dev_kit_root_make_help
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index 000bbf501..d208547ec 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -62,6 +62,30 @@ New Features
 
   * Added support for matching on IPv4 Time To Live and IPv6 Hop Limit.
 
+* **Added rte_graph library.**
+
+  Graph architecture abstracts the data processing functions as a ``node`` and
+  ``links`` them together to create a complex ``graph`` to enable reusable/modular
+  data processing functions. The graph library provides API to enable graph
+  framework operations such as create, lookup, dump and destroy on graph and node
+  operations such as clone, edge update, and edge shrink, etc.
+  The API also allows to create the stats cluster to monitor per graph and per node stats.
+
+* **Added rte_node library which consists of a set of packet processing nodes.**
+
+  The rte_node library that consists of nodes used by rte_graph library. Each
+  node performs a specific packet processing function based on application
+  configuration. The following nodes are added:
+
+  * Null node: Skeleton node that defines the general structure of a node.
+  * Ethernet device node: Consists of ethernet Rx/Tx nodes as well as ethernet
+    control APIs.
+  * IPv4 lookup node: Consists of ipv4 extract and lpm lookup node. Routes can
+    be configured by the application through ``rte_node_ip4_route_add`` function.
+  * IPv4 rewrite node: Consists of ipv4 and ethernet header rewrite functionality
+    that can be configured through ``rte_node_ip4_rewrite_add`` function.
+  * Packet drop node: Frees the packets received to their respective mempool.
+
 
 Removed Items
 -------------
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v3 29/29] doc: add l3fwd graph application user guide
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (27 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 28/29] doc: add graph library programmer's guide guide jerinj
@ 2020-03-31 19:29     ` jerinj
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
  29 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-03-31 19:29 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic, Ori Kam,
	Bruce Richardson, Radu Nicolau, Akhil Goyal, Tomasz Kantecki,
	Sunil Kumar Kori, Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Adding the user guide for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                                   |   1 +
 doc/guides/rel_notes/release_20_05.rst        |   8 +
 doc/guides/sample_app_ug/index.rst            |   1 +
 doc/guides/sample_app_ug/intro.rst            |   4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst | 327 ++++++++++++++++++
 5 files changed, 341 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 1d2cf6caa..e8a87e119 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1583,6 +1583,7 @@ F: doc/guides/sample_app_ug/l3_forward.rst
 
 M: Nithin Dabilpuram <ndabilpuram@marvell.com>
 F: examples/l3fwd-graph/
+F: doc/guides/sample_app_ug/l3_forward_graph.rst
 
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index d208547ec..9d3e7bd69 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -86,6 +86,14 @@ New Features
     that can be configured through ``rte_node_ip4_rewrite_add`` function.
   * Packet drop node: Frees the packets received to their respective mempool.
 
+* **Added new l3fwd-graph sample application.**
+
+  Added an example application ``l3fwd-graph``. It demonstrates the usage of graph
+  library and node library for packet processing. In addition to the library usage
+  demonstration, this application can use for performance comparison with existing
+  ``l3fwd`` (The static code without any nodes) with the modular ``l3fwd-graph``
+  approach.
+
 
 Removed Items
 -------------
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index ac3445147..cf9c1e44d 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -29,6 +29,7 @@ Sample Applications User Guides
     l2_forward_event
     l2_forward_cat
     l3_forward
+    l3_forward_graph
     l3_forward_power_man
     l3_forward_access_ctrl
     link_status_intr
diff --git a/doc/guides/sample_app_ug/intro.rst b/doc/guides/sample_app_ug/intro.rst
index 6cd0342a1..8ff223b16 100644
--- a/doc/guides/sample_app_ug/intro.rst
+++ b/doc/guides/sample_app_ug/intro.rst
@@ -54,6 +54,10 @@ examples are highlighted below.
   forwarding, or ``l3fwd`` application does forwarding based on Internet
   Protocol, IPv4 or IPv6 like a simple router.
 
+* :doc:`Network Layer 3 forwarding Graph<l3_forward_graph>`: The Network Layer3
+  forwarding Graph, or ``l3fwd_graph`` application does forwarding based on IPv4
+  like a simple router with DPDK Graph framework.
+
 * :doc:`Hardware packet copying<ioat>`: The Hardware packet copying,
   or ``ioatfwd`` application demonstrates how to use IOAT rawdev driver for
   copying packets between two threads.
diff --git a/doc/guides/sample_app_ug/l3_forward_graph.rst b/doc/guides/sample_app_ug/l3_forward_graph.rst
new file mode 100644
index 000000000..73153f82b
--- /dev/null
+++ b/doc/guides/sample_app_ug/l3_forward_graph.rst
@@ -0,0 +1,327 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+L3 Forwarding Graph Sample Application
+======================================
+
+The L3 Forwarding Graph application is a simple example of packet processing
+using the DPDK Graph framework. The application performs L3 forwarding using
+Graph framework and nodes written for graph framework.
+
+Overview
+--------
+
+The application demonstrates the use of the graph framework and graph nodes
+``ethdev_rx``, ``ip4_lookup``, ``ip4_rewrite``, ``ethdev_tx`` and ``pkt_drop`` in DPDK to
+implement packet forwarding.
+
+The initialization is very similar to those of the :doc:`l3_forward`.
+There is also additional initialization of graph for graph object creation
+and configuration per lcore.
+Run-time path is main thing that differs from L3 forwarding sample application.
+Difference is that forwarding logic starting from Rx, followed by LPM lookup,
+TTL update and finally Tx is implemented inside graph nodes. These nodes are
+interconnected in graph framework. Application main loop needs to walk over
+graph using ``rte_graph_walk()`` with graph objects created one per slave lcore.
+
+The lookup method is as per implementation of ``ip4_lookup`` graph node.
+The ID of the output interface for the input packet is the next hop returned by
+the LPM lookup. The set of LPM rules used by the application is statically
+configured and provided to ``ip4_lookup`` graph node and ``ip4_rewrite`` graph node
+using node control API ``rte_node_ip4_route_add()`` and ``rte_node_ip4_rewrite_add()``.
+
+In the sample application, only IPv4 forwarding is supported as of now.
+
+Compiling the Application
+-------------------------
+
+To compile the sample application see :doc:`compiling`.
+
+The application is located in the ``l3fwd-graph`` sub-directory.
+
+Running the Application
+-----------------------
+
+The application has a number of command line options similar to l3fwd::
+
+    ./l3fwd-graph [EAL options] -- -p PORTMASK
+                                   [-P]
+                                   --config(port,queue,lcore)[,(port,queue,lcore)]
+                                   [--eth-dest=X,MM:MM:MM:MM:MM:MM]
+                                   [--enable-jumbo [--max-pkt-len PKTLEN]]
+                                   [--no-numa]
+                                   [--per-port-pool]
+
+Where,
+
+* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
+
+* ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
+  Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
+
+* ``--config (port,queue,lcore)[,(port,queue,lcore)]:`` Determines which queues from which ports are mapped to which cores.
+
+* ``--eth-dest=X,MM:MM:MM:MM:MM:MM:`` Optional, ethernet destination for port X.
+
+* ``--enable-jumbo:`` Optional, enables jumbo frames.
+
+* ``--max-pkt-len:`` Optional, under the premise of enabling jumbo, maximum packet length in decimal (64-9600).
+
+* ``--no-numa:`` Optional, disables numa awareness.
+
+* ``--per-port-pool:`` Optional, set to use independent buffer pools per port. Without this option, single buffer pool is used for all ports.
+
+For example, consider a dual processor socket platform with 8 physical cores, where cores 0-7 and 16-23 appear on socket 0,
+while cores 8-15 and 24-31 appear on socket 1.
+
+To enable L3 forwarding between two ports, assuming that both ports are in the same socket, using two cores, cores 1 and 2,
+(which are in the same socket too), use the following command:
+
+.. code-block:: console
+
+    ./build/l3fwd-graph -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+
+In this command:
+
+*   The -l option enables cores 1, 2
+
+*   The -p option enables ports 0 and 1
+
+*   The --config option enables one queue on each port and maps each (port,queue) pair to a specific core.
+    The following table shows the mapping in this example:
+
++----------+-----------+-----------+-------------------------------------+
+| **Port** | **Queue** | **lcore** | **Description**                     |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 0        | 0         | 1         | Map queue 0 from port 0 to lcore 1. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 1        | 0         | 2         | Map queue 0 from port 1 to lcore 2. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+
+Refer to the *DPDK Getting Started Guide* for general information on running applications and
+the Environment Abstraction Layer (EAL) options.
+
+.. _l3_fwd_graph_explanation:
+
+Explanation
+-----------
+
+The following sections provide some explanation of the sample application code.
+As mentioned in the overview section, the initialization is similar to that of
+the :doc:`l3_forward`. Run-time path though similar in functionality to that of
+:doc:`l3_forward`, major part of the implementation is in graph nodes via used
+via ``librte_node`` library.
+The following sections describe aspects that are specific to the L3 Forwarding
+Graph sample application.
+
+Graph Node Pre-Init Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After device configuration and device Rx, Tx queue setup is complete,
+a minimal config of port id, num_rx_queues, num_tx_queues, mempools etc will
+be passed to *ethdev_** node ctrl API ``rte_node_eth_config()``. This will be
+lead to the clone of ``ethdev_rx`` and ``ethdev_tx`` nodes as ``ethdev_rx-X-Y`` and
+``ethdev_tx-X`` where X, Y represent port id and queue id associated with them.
+In case of ``ethdev_tx-X`` nodes, tx queue id assigned per instance of the node
+is same as graph id.
+
+These cloned nodes along with existing static nodes such as ``ip4_lookup`` and
+``ip4_rewrite`` will be used in graph creation to associate node's to lcore
+specific graph object.
+
+.. code-block:: c
+
+    RTE_ETH_FOREACH_DEV(portid)
+    {
+
+        /* ... */
+        ret = rte_eth_dev_configure(portid, nb_rx_queue,
+                                    n_tx_queue, &local_port_conf);
+        /* ... */
+
+        /* Init one TX queue per couple (lcore,port) */
+        queueid = 0;
+        for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+            /* ... */
+            ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+                                         socketid, txconf);
+            /* ... */
+            queueid++;
+        }
+
+        /* Setup ethdev node config */
+        ethdev_conf[nb_conf].port_id = portid;
+        ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+        ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+        if (!per_port_pool)
+            ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+        else
+          ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+        ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+        nb_conf++;
+        printf("\n");
+    }
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* Init RX queues */
+        for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+            /* ... */
+            if (!per_port_pool)
+                ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                             &rxq_conf, pktmbuf_pool[0][socketid]);
+            else
+              ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                           &rxq_conf, pktmbuf_pool[portid][socketid]);
+            /* ... */
+        }
+    }
+
+    /* Ethdev node config, skip rx queue mapping */
+    ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+
+Graph Initialization
+~~~~~~~~~~~~~~~~~~~~
+
+Now a graph needs to be created with a specific set of nodes for every lcore.
+A graph object returned after graph creation is a per lcore object and
+cannot be shared between lcores. Since ``ethdev_tx-X`` node is per port node,
+it can be associated with all the graphs created as all the lcores should have
+Tx capability for every port. But ``ethdev_rx-X-Y`` node is created per
+(port, rx_queue_id), so they should be associated with a graph based on
+the application argument ``--config`` specifying rx queue mapping to lcore.
+
+.. note::
+
+    The Graph creation will fail if the passed set of shell node pattern's
+    are not sufficient to meet their inter-dependency or even one node is not
+    found with a given regex node pattern.
+
+.. code-block:: c
+
+    static const char *node_patterns[] = {
+        "ip4*",
+        "ethdev_tx-*",
+        "pkt_drop",
+    };
+    uint16_t nb_patterns = RTE_DIM(node_patterns);
+
+    /* ... */
+
+    /* Create a graph object per lcore with common nodes and
+     * lcore specific nodes based on application arguments
+     */
+    memset(&graph_conf, 0, sizeof(graph_conf));
+
+    /* Common set of nodes in every lcore's graph object */
+    graph_conf.node_patterns = node_patterns;
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* ... */
+
+        /* Skip graph creation if no source exists */
+        if (!qconf->n_rx_queue)
+            continue;
+
+        /* Add rx node patterns of this lcore based on --config */
+        for (i = 0; i < qconf->n_rx_queue; i++) {
+            graph_conf.node_patterns[nb_patterns + i] =
+                                qconf->rx_queue_list[i].node_name;
+        }
+
+        graph_conf.nb_node_patterns = nb_patterns + i;
+        graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+        snprintf(qconf->name, sizeof(qconf->name), "worker_%u", lcore_id);
+
+        graph_id = rte_graph_create(qconf->name, &graph_conf);
+
+        /* ... */
+
+        qconf->graph = rte_graph_lookup(qconf->name);
+
+        /* ... */
+    }
+
+Forwarding data(Route, Next-Hop) addition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once graph objects are created, node specific info like routes and rewrite
+headers will be provided run-time using ``rte_node_ip4_route_add()`` and
+``rte_node_ip4_rewrite_add()`` API.
+
+.. note::
+
+    Since currently ``ip4_lookup`` and ``ip4_rewrite`` nodes don't support
+    lock-less mechanisms(RCU, etc) to add run-time forwarding data like route and
+    rewrite data, forwarding data is added before packet processing loop is
+    launched on slave lcore.
+
+.. code-block:: c
+
+    /* Add route to ip4 graph infra */
+    for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+        /* ... */
+
+        dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+        next_hop = i;
+
+        /* ... */
+        ret = rte_node_ip4_route_add(ipv4_l3fwd_lpm_route_array[i].ip,
+                                     ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+                                     RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+        /* ... */
+
+        memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+        /* Add next hop for a given destination */
+        ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+                                       rewrite_len, dst_port);
+
+        RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+                route_str, next_hop);
+    }
+
+Packet Forwarding using Graph Walk
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that all the device configurations are done, graph creations are done and
+forwarding data is updated with nodes, slave lcores will be launched with graph
+main loop. Graph main loop is very simple in the sense that it needs to
+continuously call a non-blocking API ``rte_graph_walk()`` with it's lcore
+specific graph object that was already created.
+
+.. note::
+
+    rte_graph_walk() will walk over all the sources nodes i.e ``ethdev_rx-X-Y``
+    associated with a given graph and Rx the available packets and enqueue them
+    to the following node ``ip4_lookup`` which then will enqueue them to ``ip4_rewrite``
+    node if LPM lookup succeeds. ``ip4_rewrite`` node then will update Ethernet header
+    as per next-hop data and transmit the packet via port 'Z' by enqueuing
+    to ``ethdev_tx-Z`` node instance in its graph object.
+
+.. code-block:: c
+
+    /* Main processing loop */
+    static int
+    graph_main_loop(void *conf)
+    {
+        // ...
+
+        lcore_id = rte_lcore_id();
+        qconf = &lcore_conf[lcore_id];
+        graph = qconf->graph;
+
+        RTE_LOG(INFO, L3FWD_GRAPH,
+                "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+                qconf->name, graph);
+
+        /* Walk over graph until signal to quit */
+        while (likely(!force_quit))
+            rte_graph_walk(graph);
+        return 0;
+    }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support jerinj
@ 2020-04-03  9:26       ` Wang, Xiao W
  2020-04-04 12:15         ` Jerin Jacob
  2020-04-06 12:36       ` Andrzej Ostruszka
  1 sibling, 1 reply; 219+ messages in thread
From: Wang, Xiao W @ 2020-04-03  9:26 UTC (permalink / raw)
  To: jerinj, Thomas Monjalon, Richardson, Bruce, Mcnamara, John,
	Kovacevic, Marko, Kiran Kumar K
  Cc: dev, david.marchand, mdr, mattias.ronnblom, pbhagavatula, ndabilpuram

Hi Jerin,

Found small typos on this patch. Please check comments inline.

Best Regards,
Xiao

> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of jerinj@marvell.com
> Sent: Wednesday, April 1, 2020 3:29 AM
> To: Thomas Monjalon <thomas@monjalon.net>; Richardson, Bruce
> <bruce.richardson@intel.com>; Mcnamara, John <john.mcnamara@intel.com>;
> Kovacevic, Marko <marko.kovacevic@intel.com>; Jerin Jacob
> <jerinj@marvell.com>; Kiran Kumar K <kirankumark@marvell.com>
> Cc: dev@dpdk.org; david.marchand@redhat.com; mdr@ashroe.eu;
> mattias.ronnblom@ericsson.com; pbhagavatula@marvell.com;
> ndabilpuram@marvell.com
> Subject: [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph
> support
> 
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Graph architecture abstracts the data processing functions as
> "node" and "link" them together to create a complex "graph" to enable
> reusable/modular data processing functions.
> 
> These APIs enables graph framework operations such as create, lookup,
> dump and destroy on graph and node operations such as clone,
> edge update, and edge shrink, etc. The API also allows creating the
> stats cluster to monitor per graph and per node stats.
> 
> This patch defines the public API for graph support.
> This patch also adds support for the build infrastructure and
> update the MAINTAINERS file for the graph subsystem.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
>  MAINTAINERS                            |   5 +
>  config/common_base                     |   7 +
>  config/rte_config.h                    |   4 +
>  doc/api/doxy-api-index.md              |   1 +
>  doc/api/doxy-api.conf.in               |   1 +
>  lib/Makefile                           |   3 +
>  lib/librte_graph/Makefile              |  22 +
>  lib/librte_graph/graph.c               |   5 +
>  lib/librte_graph/meson.build           |  11 +
>  lib/librte_graph/rte_graph.h           | 786 +++++++++++++++++++++++++
>  lib/librte_graph/rte_graph_version.map |   3 +
>  lib/meson.build                        |   2 +-
>  mk/rte.app.mk                          |   1 +
>  13 files changed, 850 insertions(+), 1 deletion(-)
>  create mode 100644 lib/librte_graph/Makefile
>  create mode 100644 lib/librte_graph/graph.c
>  create mode 100644 lib/librte_graph/meson.build
>  create mode 100644 lib/librte_graph/rte_graph.h
>  create mode 100644 lib/librte_graph/rte_graph_version.map
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index db235c2cc..bc7085983 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1469,6 +1469,11 @@ F: examples/bpf/
>  F: app/test/test_bpf.c
>  F: doc/guides/prog_guide/bpf_lib.rst
> 
> +Graph - EXPERIMENTAL
> +M: Jerin Jacob <jerinj@marvell.com>
> +M: Kiran Kumar K <kirankumark@marvell.com>
> +F: lib/librte_graph/
> +
[...]

> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Create graph stats cluster to aggregate runtime node stats.
> + *
> + * @param prm
> + *   Parameters including file pointer to dump stats,
> + *   Graph pattern to create cluster and callback function.
> + *
> + * @return
> + *   Valid pointer on success, NULL otherwise.
> + */
> +__rte_experimental
> +struct rte_graph_cluster_stats *rte_graph_cluster_stats_create(
> +			const struct rte_graph_cluster_stats_param *prm);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Destroy cluster stats.
> + *
> + * @param stat
> + *    Valid cluster pointer to destroy.
> + *
This empty line can be removed.

> + */
> +__rte_experimental
> +void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
[...]

> +#define RTE_NODE_REGISTER(node)                                                \
> +	RTE_INIT(rte_node_register_##node)                                     \
> +	{                                                                      \
> +		node.parent_id = RTE_NODE_ID_INVALID;                          \
> +		node.id = __rte_node_register(&node);                          \
> +	}
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Clone a node from static node(node created from RTE_NODE_REGISTER).
> + *
> + * @param id
> + *   Static node id to clone from.
> + * @param name
> + *   Name of the new node. The library prepends the parent node name to the
> + * user-specified the name. The final node name will be,
Change it to "the user-specified name".


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 02/29] graph: implement node registration
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 02/29] graph: implement node registration jerinj
@ 2020-04-03 10:44       ` Wang, Xiao W
  2020-04-04 12:29         ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Wang, Xiao W @ 2020-04-03 10:44 UTC (permalink / raw)
  To: jerinj, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

Hi,

Comments inline.

Best Regards,
Xiao

> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of jerinj@marvell.com
> Sent: Wednesday, April 1, 2020 3:29 AM
> To: Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K
> <kirankumark@marvell.com>
> Cc: dev@dpdk.org; thomas@monjalon.net; david.marchand@redhat.com;
> mdr@ashroe.eu; mattias.ronnblom@ericsson.com;
> pbhagavatula@marvell.com; ndabilpuram@marvell.com
> Subject: [dpdk-dev] [PATCH v3 02/29] graph: implement node registration
> 
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding rte_node_register() API implementation includes allocating
> memory for node object, check for duplicate node name and
> add the allocated node to STAILQ node_list for future use.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
>  lib/librte_graph/Makefile              |   1 +
>  lib/librte_graph/graph.c               |  18 +++-
>  lib/librte_graph/graph_private.h       |  75 ++++++++++++++++
>  lib/librte_graph/meson.build           |   2 +-
>  lib/librte_graph/node.c                | 115 +++++++++++++++++++++++++
>  lib/librte_graph/rte_graph_version.map |   4 +
>  6 files changed, 213 insertions(+), 2 deletions(-)
>  create mode 100644 lib/librte_graph/graph_private.h
>  create mode 100644 lib/librte_graph/node.c
> 
> diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
> index 26fe514f3..933d0ee49 100644
> --- a/lib/librte_graph/Makefile
> +++ b/lib/librte_graph/Makefile
> @@ -14,6 +14,7 @@ LDLIBS += -lrte_eal
>  EXPORT_MAP := rte_graph_version.map
> 
[...]

> index 000000000..7999ca6ed
> --- /dev/null
> +++ b/lib/librte_graph/node.c
> @@ -0,0 +1,115 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2020 Marvell International Ltd.
> + */
> +
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_debug.h>
> +#include <rte_eal.h>
> +#include <rte_errno.h>
> +#include <rte_string_fns.h>
> +
> +#include "graph_private.h"
> +
> +static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
> +static rte_node_t node_id;
> +
> +#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)

It's better to move this MACRO into the next patch since it's first used there, and ID_CHECK is defined there.

> +
> +/* Private functions */


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 03/29] graph: implement node operations
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 03/29] graph: implement node operations jerinj
@ 2020-04-03 10:54       ` Wang, Xiao W
  2020-04-04 13:07         ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Wang, Xiao W @ 2020-04-03 10:54 UTC (permalink / raw)
  To: jerinj, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

Hi Jerin,

Comment inline.

Best Regards,
Xiao

> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of jerinj@marvell.com
> Sent: Wednesday, April 1, 2020 3:29 AM
> To: Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K
> <kirankumark@marvell.com>
> Cc: dev@dpdk.org; thomas@monjalon.net; david.marchand@redhat.com;
> mdr@ashroe.eu; mattias.ronnblom@ericsson.com;
> pbhagavatula@marvell.com; ndabilpuram@marvell.com
> Subject: [dpdk-dev] [PATCH v3 03/29] graph: implement node operations
> 
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding node-specific API implementation like cloning node, updating
> edges for the node, shrinking edges of a node, retrieving edges of a
> node.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
>  lib/librte_graph/graph_private.h       |  10 +
>  lib/librte_graph/node.c                | 269 +++++++++++++++++++++++++
>  lib/librte_graph/rte_graph_version.map |  10 +
>  3 files changed, 289 insertions(+)
> 
> diff --git a/lib/librte_graph/graph_private.h
> b/lib/librte_graph/graph_private.h
> index 8b9ff5292..7ed6d01b6 100644
> --- a/lib/librte_graph/graph_private.h
> +++ b/lib/librte_graph/graph_private.h
> @@ -13,6 +13,16 @@
> 
>  #include "rte_graph.h"
> 
> +
> +#define ID_CHECK(id, id_max)                                                   \
> +	do {                                                                   \
> +		if ((id) >= (id_max)) {                                        \
> +			rte_errno = EINVAL;                                    \
> +			goto fail;                                             \
> +		}                                                              \
> +	} while (0)
> +
> +
[...]
> +char *
> +rte_node_id_to_name(rte_node_t id)
> +{
> +	struct node *node;
> +
> +	NODE_ID_CHECK(id);
> +	STAILQ_FOREACH(node, &node_list, next)
> +		if (node->id == id)
> +			return node->name;
> +
> +fail:
> +	return NULL;
> +}
> +
> +rte_edge_t
> +rte_node_edge_count(rte_node_t id)
> +{
> +	struct node *node;
> +
> +	NODE_ID_CHECK(id);
> +	STAILQ_FOREACH(node, &node_list, next)
> +		if (node->id == id)
> +			return node->nb_edges;
> +fail:
> +	return RTE_EDGE_ID_INVALID;
> +}
> +
> +static rte_edge_t
> +edge_update(struct node *node, struct node *prev, rte_edge_t from,
> +	    const char **next_nodes, rte_edge_t nb_edges)
> +{
> +	rte_edge_t i, max_edges, count = 0;
> +	struct node *new_node;
> +	bool need_realloc;
> +	size_t sz;
> +
> +	if (from == RTE_EDGE_ID_INVALID)
> +		from = node->nb_edges;
> +
> +	/* Don't create hole in next_nodes[] list */
> +	if (from > node->nb_edges) {
> +		rte_errno = ENOMEM;
> +		goto fail;
> +	}
> +
> +	/* Remove me from list */
> +	STAILQ_REMOVE(&node_list, node, node, next);
> +
> +	/* Allocate the storage space for new node if required */
> +	max_edges = from + nb_edges;
> +	need_realloc = max_edges > node->nb_edges;
> +	if (need_realloc) {
> +		sz = sizeof(struct node) + (max_edges *
> RTE_NODE_NAMESIZE);
> +		new_node = realloc(node, sz);
> +		if (new_node == NULL) {
> +			rte_errno = ENOMEM;
> +			goto restore;
> +		} else {
> +			node = new_node;
> +		}
> +	}
> +
> +	/* Update the new nodes name */
> +	for (i = from; i < max_edges; i++, count++) {
> +		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
> +				RTE_NODE_NAMESIZE) < 0) {
> +			rte_errno = E2BIG;
> +			goto restore;
> +		}
> +	}
> +restore:
> +	/* Update the linked list to point new node address in prev node */
> +	if (prev)
> +		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
> +	else
> +		STAILQ_INSERT_HEAD(&node_list, node, next);
> +
> +	if (need_realloc)
> +		node->nb_edges += count;

If the "from" starts from somewhere in the middle of the edges, and also triggers a realloc,
then the new edge number should be: node->nb_edges = max_edges;

> +
> +fail:
> +	return count;


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 04/29] graph: implement node debug routines
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 04/29] graph: implement node debug routines jerinj
@ 2020-04-04  7:57       ` Wang, Xiao W
  2020-04-04 13:12         ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Wang, Xiao W @ 2020-04-04  7:57 UTC (permalink / raw)
  To: jerinj, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

Hi,

Comment inline.

Best Regards,
Xiao

> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of jerinj@marvell.com
> Sent: Wednesday, April 1, 2020 3:29 AM
> To: Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K
> <kirankumark@marvell.com>
> Cc: dev@dpdk.org; thomas@monjalon.net; david.marchand@redhat.com;
> mdr@ashroe.eu; mattias.ronnblom@ericsson.com;
> pbhagavatula@marvell.com; ndabilpuram@marvell.com
> Subject: [dpdk-dev] [PATCH v3 04/29] graph: implement node debug routines
> 
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding node debug API implementation support to dump
> single or all the node objects to the given file.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
>  lib/librte_graph/Makefile              |  1 +
>  lib/librte_graph/graph_debug.c         | 25 ++++++++++++++++++++
>  lib/librte_graph/graph_private.h       | 12 ++++++++++
>  lib/librte_graph/meson.build           |  2 +-
>  lib/librte_graph/node.c                | 32 ++++++++++++++++++++++++++
>  lib/librte_graph/rte_graph_version.map |  1 +
>  6 files changed, 72 insertions(+), 1 deletion(-)
>  create mode 100644 lib/librte_graph/graph_debug.c
> 
> diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
> index 933d0ee49..2a6d86933 100644
> --- a/lib/librte_graph/Makefile
> +++ b/lib/librte_graph/Makefile
> @@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
>  # all source are stored in SRCS-y
>  SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
>  SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
> +SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
> 
[...]
> diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
> index 5754ac23b..01512182f 100644
> --- a/lib/librte_graph/meson.build
> +++ b/lib/librte_graph/meson.build
> @@ -4,7 +4,7 @@
> 
>  name = 'graph'
> 
> -sources = files('node.c', 'graph.c')
> +sources = files('node.c', 'graph.c', 'graph_debug.c')
>  headers = files('rte_graph.h')
>  allow_experimental_apis = true
> 
> diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
> index 8de857889..2f9c2ea4c 100644
> --- a/lib/librte_graph/node.c
> +++ b/lib/librte_graph/node.c
> @@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char
> *next_nodes[])
>  	return rc;
>  }
> 
> +static void
> +node_scan_dump(FILE *f, rte_node_t id, bool all)
> +{
> +	struct node *node;
> +
> +	RTE_ASSERT(f != NULL);
> +	NODE_ID_CHECK(id);
> +
> +	STAILQ_FOREACH(node, &node_list, next) {
> +		if (all == true) {
> +			node_dump(f, node);
> +		} else if (node->id == id) {
> +			node_dump(f, node);
> +			return;
> +		}
> +	}
> +fail:
we can remove this "fail" mark since it's not used as jmp target.

> +	return;
> +}


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support
  2020-04-03  9:26       ` Wang, Xiao W
@ 2020-04-04 12:15         ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-04 12:15 UTC (permalink / raw)
  To: Wang, Xiao W
  Cc: jerinj, Thomas Monjalon, Richardson, Bruce, Mcnamara, John,
	Kovacevic, Marko, Kiran Kumar K, dev, david.marchand, mdr,
	mattias.ronnblom, pbhagavatula, ndabilpuram

On Fri, Apr 3, 2020 at 2:56 PM Wang, Xiao W <xiao.w.wang@intel.com> wrote:
>
> Hi Jerin,

Hi Xiao. Thanks for the review.

> Found small typos on this patch. Please check comments inline.
>
> Best Regards,
> Xiao
>
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Destroy cluster stats.
> > + *
> > + * @param stat
> > + *    Valid cluster pointer to destroy.
> > + *
> This empty line can be removed.

Will fix in v4.

> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Clone a node from static node(node created from RTE_NODE_REGISTER).
> > + *
> > + * @param id
> > + *   Static node id to clone from.
> > + * @param name
> > + *   Name of the new node. The library prepends the parent node name to the
> > + * user-specified the name. The final node name will be,
> Change it to "the user-specified name".

Will fix in v4.

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 02/29] graph: implement node registration
  2020-04-03 10:44       ` Wang, Xiao W
@ 2020-04-04 12:29         ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-04 12:29 UTC (permalink / raw)
  To: Wang, Xiao W
  Cc: jerinj, Kiran Kumar K, dev, thomas, david.marchand, mdr,
	mattias.ronnblom, pbhagavatula, ndabilpuram

> > +
> > +static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
> > +static rte_node_t node_id;
> > +
> > +#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)
>
> It's better to move this MACRO into the next patch since it's first used there, and ID_CHECK is defined there.

Will fix in v4.

>
> > +
> > +/* Private functions */
>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 03/29] graph: implement node operations
  2020-04-03 10:54       ` Wang, Xiao W
@ 2020-04-04 13:07         ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-04 13:07 UTC (permalink / raw)
  To: Wang, Xiao W
  Cc: jerinj, Kiran Kumar K, dev, thomas, david.marchand, mdr,
	mattias.ronnblom, pbhagavatula, ndabilpuram

On Fri, Apr 3, 2020 at 4:24 PM Wang, Xiao W <xiao.w.wang@intel.com> wrote:
> > +
> > +static rte_edge_t
> > +edge_update(struct node *node, struct node *prev, rte_edge_t from,
> > +         const char **next_nodes, rte_edge_t nb_edges)
> > +{
[..]
> > +     /* Update the linked list to point new node address in prev node */
> > +     if (prev)
> > +             STAILQ_INSERT_AFTER(&node_list, prev, node, next);
> > +     else
> > +             STAILQ_INSERT_HEAD(&node_list, node, next);
> > +
> > +     if (need_realloc)
> > +             node->nb_edges += count;
>
> If the "from" starts from somewhere in the middle of the edges, and also triggers a realloc,
> then the new edge number should be: node->nb_edges = max_edges;

Agree. I will fix it in v4.

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 04/29] graph: implement node debug routines
  2020-04-04  7:57       ` Wang, Xiao W
@ 2020-04-04 13:12         ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-04 13:12 UTC (permalink / raw)
  To: Wang, Xiao W
  Cc: jerinj, Kiran Kumar K, dev, thomas, david.marchand, mdr,
	mattias.ronnblom, pbhagavatula, ndabilpuram

On Sat, Apr 4, 2020 at 1:27 PM Wang, Xiao W <xiao.w.wang@intel.com> wrote:
> > +static void
> > +node_scan_dump(FILE *f, rte_node_t id, bool all)
> > +{
> > +     struct node *node;
> > +
> > +     RTE_ASSERT(f != NULL);
> > +     NODE_ID_CHECK(id);

This one is using fail: jump. See below

> > +     STAILQ_FOREACH(node, &node_list, next) {
> > +             if (all == true) {
> > +                     node_dump(f, node);
> > +             } else if (node->id == id) {
> > +                     node_dump(f, node);
> > +                     return;
> > +             }
> > +     }
> > +fail:
> we can remove this "fail" mark since it's not used as jmp target.

See above.

>
> > +     return;
> > +}
>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 00/29] graph: introduce graph subsystem
  2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
                       ` (28 preceding siblings ...)
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 29/29] doc: add l3fwd graph application user guide jerinj
@ 2020-04-05  8:55     ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 01/29] graph: define the public API for graph support jerinj
                         ` (30 more replies)
  29 siblings, 31 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Using graph traversal for packet processing is a proven architecture
that has been implemented in various open source libraries.

Graph architecture for packet processing enables abstracting the data
processing functions as “nodes” and “links” them together to create a
complex “graph” to create reusable/modular data processing functions. 

The patchset further includes performance enhancements and modularity
to the DPDK as discussed in more detail below.

v4..v3:
-------
Addressed the following review comments from Wang, Xiao W

1) Remove unnecessary line from rte_graph.h
2) Fix a typo from rte_graph.h
3) Move NODE_ID_CHECK to 3rd patch where it is first used.
4) Fixed bug in edge_update()

v3..v2:
-------
1) refactor ipv4 node lookup by moving SSE and NEON specific code to
lib/librte_node/ip4_lookup_sse.h and lib/librte_node/ip4_lookup_neon.h
2) Add scalar version of process() function for ipv4 lookup to make
the node work on NON x86 and arm64 machines.

v2..v1:
------
1) Added programmer guide/implementation documentation and l3fwd-graph doc

RFC..v1:
--------

1) Split the patch to more logical ones for review.
2) Added doxygen comments for the API
3) Code cleanup
4) Additional performance improvements.
Delta between l3fwd and l3fwd-graph is negligible now.
(~1%) on octeontx2.
5) Added SIMD routines for x86 in additional to arm64.

Hosted in netlify for easy reference:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Programmer’s Guide:
https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html

l3fwd-graph doc:
https://dpdk-graph.netlify.com/doc/html/guides/sample_app_ug/l3_forward_graph.html

API doc:
https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__graph__worker_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__eth__api_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__ip4__api_8h.html

2) Added the release notes for the this feature

3) Fix build issues reported by CI for v1:
http://mails.dpdk.org/archives/test-report/2020-March/121326.html


Addional nodes planned for v20.08
----------------------------------
1) Packet classification node
2) Support for IPV6 LPM node


This patchset contains
-----------------------------
1) The API definition to "create" nodes and "link" together to create a
"graph" for packet processing. See, lib/librte_graph/rte_graph.h  

2) The Fast path API definition for the graph walker and enqueue
function used by the workers. See, lib/librte_graph/rte_graph_worker.h

3) Optimized SW implementation for (1) and (2). See, lib/librte_graph/

4) Test case to verify the graph infrastructure functionality
See, app/test/test_graph.c
 
5) Performance test cases to evaluate the cost of graph walker and nodes
enqueue fast-path function for various combinations.

See app/test/test_graph_perf.c

6) Packet processing nodes(Null, Rx, Tx, Pkt drop, IPV4 rewrite, IPv4
lookup)
using graph infrastructure. See lib/librte_node/*

7) An example application to showcase l3fwd
(functionality same as existing examples/l3fwd) using graph
infrastructure and use packets processing nodes (item (6)). See examples/l3fwd-graph/.

Performance
-----------
1) Graph walk and node enqueue overhead can be tested with performance
test case application [1]
# If all packets go from a node to another node (we call it as
# "homerun") then it will be just a pointer swap for a burst of packets.
# In the worst case, a couple of handful cycles to move an object from a
node to another node.

2) Performance comparison with existing l3fwd (The complete static code
with out any nodes) vs modular l3fwd-graph with 5 nodes
(ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
Here is graphical representation of the l3fwd-graph as Graphviz dot
file: 
http://bit.ly/39UPPGm

# l3fwd-graph performance is -1.2% wrt static l3fwd.

# We have simulated the similar test with existing librte_pipeline
# application [4].
ip_pipline application is -48.62% wrt static l3fwd.

The above results are on octeontx2. It may vary on other platforms.
The platforms with higher L1 and L2 caches will have further better
performance.


Tested architectures:
--------------------
1) AArch64
2) X86


Identified tweaking for better performance on different targets
---------------------------------------------------------------
1) Test with various burst size values (256, 128, 64, 32) using
CONFIG_RTE_GRAPH_BURST_SIZE config option.
Based on our testing, on x86 and arm64 servers, The sweet spot is 256
burst size.
While on arm64 embedded SoCs, it is either 64 or 128.

2) Disable node statistics (use CONFIG_RTE_LIBRTE_GRAPH_STATS config
option)
if not needed.

3) Use arm64 optimized memory copy for arm64 architecture by
selecting CONFIG_RTE_ARCH_ARM64_MEMCPY. 

Commands to run tests
---------------------

[1] 
perf test:
echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[2]
functionality test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[3]
l3fwd-graph:
./l3fwd-graph -c 0x100  -- -p 0x3 --config="(0, 0, 8)" -P

[4]
# ./ip_pipeline --c 0xff0000 -- -s route.cli

Route.cli: (Copy paste to the shell to avoid dos format issues)

https://pastebin.com/raw/B4Ktx7TT


Jerin Jacob (13):
  graph: define the public API for graph support
  graph: implement node registration
  graph: implement node operations
  graph: implement node debug routines
  graph: implement internal graph operation helpers
  graph: populate fastpath memory for graph reel
  graph: implement create and destroy APIs
  graph: implement graph operation APIs
  graph: implement Graphviz export
  graph: implement debug routines
  graph: implement stats support
  graph: implement fastpath API routines
  doc: add graph library programmer's guide guide

Kiran Kumar K (2):
  graph: add unit test case
  node: add ipv4 rewrite node

Nithin Dabilpuram (11):
  node: add log infra and null node
  node: add ethdev Rx node
  node: add ethdev Tx node
  node: add ethdev Rx and Tx node ctrl API
  node: ipv4 lookup for arm64
  node: add ipv4 rewrite and lookup ctrl API
  node: add packet drop node
  l3fwd-graph: add graph based l3fwd skeleton
  l3fwd-graph: add ethdev configuration changes
  l3fwd-graph: add graph config and main loop
  doc: add l3fwd graph application user guide

Pavan Nikhilesh (3):
  graph: add performance testcase
  node: add generic ipv4 lookup node
  node: ipv4 lookup for x86

 MAINTAINERS                                   |   14 +
 app/test/Makefile                             |    7 +
 app/test/meson.build                          |   12 +-
 app/test/test_graph.c                         |  819 ++++
 app/test/test_graph_perf.c                    | 1057 ++++++
 config/common_base                            |   12 +
 config/rte_config.h                           |    4 +
 doc/api/doxy-api-index.md                     |    5 +
 doc/api/doxy-api.conf.in                      |    2 +
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   32 +
 doc/guides/sample_app_ug/index.rst            |    1 +
 doc/guides/sample_app_ug/intro.rst            |    4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst |  327 ++
 examples/Makefile                             |    3 +
 examples/l3fwd-graph/Makefile                 |   58 +
 examples/l3fwd-graph/main.c                   | 1111 ++++++
 examples/l3fwd-graph/meson.build              |   13 +
 examples/meson.build                          |    6 +-
 lib/Makefile                                  |    6 +
 lib/librte_graph/Makefile                     |   28 +
 lib/librte_graph/graph.c                      |  589 +++
 lib/librte_graph/graph_debug.c                |   84 +
 lib/librte_graph/graph_ops.c                  |  169 +
 lib/librte_graph/graph_populate.c             |  234 ++
 lib/librte_graph/graph_private.h              |  347 ++
 lib/librte_graph/graph_stats.c                |  406 ++
 lib/librte_graph/meson.build                  |   11 +
 lib/librte_graph/node.c                       |  421 +++
 lib/librte_graph/rte_graph.h                  |  785 ++++
 lib/librte_graph/rte_graph_version.map        |   47 +
 lib/librte_graph/rte_graph_worker.h           |  542 +++
 lib/librte_node/Makefile                      |   32 +
 lib/librte_node/ethdev_ctrl.c                 |  116 +
 lib/librte_node/ethdev_rx.c                   |  221 ++
 lib/librte_node/ethdev_rx_priv.h              |   81 +
 lib/librte_node/ethdev_tx.c                   |   86 +
 lib/librte_node/ethdev_tx_priv.h              |   62 +
 lib/librte_node/ip4_lookup.c                  |  216 ++
 lib/librte_node/ip4_lookup_neon.h             |  238 ++
 lib/librte_node/ip4_lookup_sse.h              |  244 ++
 lib/librte_node/ip4_rewrite.c                 |  326 ++
 lib/librte_node/ip4_rewrite_priv.h            |   77 +
 lib/librte_node/log.c                         |   14 +
 lib/librte_node/meson.build                   |   10 +
 lib/librte_node/node_private.h                |   96 +
 lib/librte_node/null.c                        |   23 +
 lib/librte_node/pkt_drop.c                    |   26 +
 lib/librte_node/rte_node_eth_api.h            |   70 +
 lib/librte_node/rte_node_ip4_api.h            |   87 +
 lib/librte_node/rte_node_version.map          |    9 +
 lib/meson.build                               |    5 +-
 meson.build                                   |    1 +
 mk/rte.app.mk                                 |    2 +
 58 files changed, 14701 insertions(+), 5 deletions(-)
 create mode 100644 app/test/test_graph.c
 create mode 100644 app/test/test_graph_perf.c
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/graph_debug.c
 create mode 100644 lib/librte_graph/graph_ops.c
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/graph_stats.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/node.c
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map
 create mode 100644 lib/librte_graph/rte_graph_worker.h
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/ip4_lookup_neon.h
 create mode 100644 lib/librte_node/ip4_lookup_sse.h
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/pkt_drop.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h
 create mode 100644 lib/librte_node/rte_node_ip4_api.h
 create mode 100644 lib/librte_node/rte_node_version.map

-- 
2.25.1


^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 01/29] graph: define the public API for graph support
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 02/29] graph: implement node registration jerinj
                         ` (29 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Thomas Monjalon, Bruce Richardson, John McNamara,
	Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Graph architecture abstracts the data processing functions as
"node" and "link" them together to create a complex "graph" to enable
reusable/modular data processing functions.

These APIs enables graph framework operations such as create, lookup,
dump and destroy on graph and node operations such as clone,
edge update, and edge shrink, etc. The API also allows creating the
stats cluster to monitor per graph and per node stats.

This patch defines the public API for graph support.
This patch also adds support for the build infrastructure and
update the MAINTAINERS file for the graph subsystem.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                            |   5 +
 config/common_base                     |   7 +
 config/rte_config.h                    |   4 +
 doc/api/doxy-api-index.md              |   1 +
 doc/api/doxy-api.conf.in               |   1 +
 lib/Makefile                           |   3 +
 lib/librte_graph/Makefile              |  22 +
 lib/librte_graph/graph.c               |   5 +
 lib/librte_graph/meson.build           |  11 +
 lib/librte_graph/rte_graph.h           | 785 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   3 +
 lib/meson.build                        |   2 +-
 mk/rte.app.mk                          |   1 +
 13 files changed, 849 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 4800f6884..aa40cc92b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1462,6 +1462,11 @@ F: examples/bpf/
 F: app/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Graph - EXPERIMENTAL
+M: Jerin Jacob <jerinj@marvell.com>
+M: Kiran Kumar K <kirankumark@marvell.com>
+F: lib/librte_graph/
+
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index c31175f9d..32f982136 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1074,6 +1074,13 @@ CONFIG_RTE_LIBRTE_BPF_ELF=n
 #
 CONFIG_RTE_LIBRTE_IPSEC=y
 
+#
+# Compile librte_graph
+#
+CONFIG_RTE_LIBRTE_GRAPH=y
+CONFIG_RTE_GRAPH_BURST_SIZE=256
+CONFIG_RTE_LIBRTE_GRAPH_STATS=y
+
 #
 # Compile the test application
 #
diff --git a/config/rte_config.h b/config/rte_config.h
index d30786bc0..e9201fd46 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -98,6 +98,10 @@
 /* KNI defines */
 #define RTE_KNI_PREEMPT_DEFAULT 1
 
+/* rte_graph defines */
+#define RTE_GRAPH_BURST_SIZE 256
+#define RTE_LIBRTE_GRAPH_STATS 1
+
 /****** driver defines ********/
 
 /* QuickAssist device */
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index dff496be0..5cc50f750 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -159,6 +159,7 @@ The public API headers are grouped by topics:
   * [pipeline]         (@ref rte_pipeline.h)
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
+  * [graph]            (@ref rte_graph.h):
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 65e8146be..e3b7f54f8 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -33,6 +33,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_eventdev \
                           @TOPDIR@/lib/librte_fib \
                           @TOPDIR@/lib/librte_flow_classify \
+                          @TOPDIR@/lib/librte_graph \
                           @TOPDIR@/lib/librte_gro \
                           @TOPDIR@/lib/librte_gso \
                           @TOPDIR@/lib/librte_hash \
diff --git a/lib/Makefile b/lib/Makefile
index 46b91ae1a..1f572b659 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -119,6 +119,9 @@ DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
 DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu
 DEPDIRS-librte_rcu := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
+DEPDIRS-librte_graph := librte_eal
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
new file mode 100644
index 000000000..26fe514f3
--- /dev/null
+++ b/lib/librte_graph/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_graph.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal
+
+EXPORT_MAP := rte_graph_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
new file mode 100644
index 000000000..a55bf443a
--- /dev/null
+++ b/lib/librte_graph/graph.c
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "rte_graph.h"
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
new file mode 100644
index 000000000..455cf2ba5
--- /dev/null
+++ b/lib/librte_graph/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+name = 'graph'
+
+sources = files('graph.c')
+headers = files('rte_graph.h')
+allow_experimental_apis = true
+
+deps += ['eal']
diff --git a/lib/librte_graph/rte_graph.h b/lib/librte_graph/rte_graph.h
new file mode 100644
index 000000000..862f51dde
--- /dev/null
+++ b/lib/librte_graph/rte_graph.h
@@ -0,0 +1,785 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_H_
+#define _RTE_GRAPH_H_
+
+/**
+ * @file rte_graph.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph architecture abstracts the data processing functions as
+ * "node" and "link" them together to create a complex "graph" to enable
+ * reusable/modular data processing functions.
+ *
+ * This API enables graph framework operations such as create, lookup,
+ * dump and destroy on graph and node operations such as clone,
+ * edge update, and edge shrink, etc. The API also allows to create the stats
+ * cluster to monitor per graph and per node stats.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_GRAPH_NAMESIZE 64 /**< Max length of graph name. */
+#define RTE_NODE_NAMESIZE 64  /**< Max length of node name. */
+#define RTE_GRAPH_OFF_INVALID UINT32_MAX /**< Invalid graph offset. */
+#define RTE_NODE_ID_INVALID UINT32_MAX   /**< Invalid node id. */
+#define RTE_EDGE_ID_INVALID UINT16_MAX   /**< Invalid edge id. */
+#define RTE_GRAPH_ID_INVALID UINT16_MAX  /**< Invalid graph id. */
+#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL /**< Graph fence data. */
+
+typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
+typedef uint32_t rte_node_t;       /**< Node id type. */
+typedef uint16_t rte_edge_t;       /**< Edge id type. */
+typedef uint16_t rte_graph_t;      /**< Graph id type. */
+
+/** Burst size in terms of log2 */
+#if RTE_GRAPH_BURST_SIZE == 1
+#define RTE_GRAPH_BURST_SIZE_LOG2 0  /**< Object burst size of 1. */
+#elif RTE_GRAPH_BURST_SIZE == 2
+#define RTE_GRAPH_BURST_SIZE_LOG2 1  /**< Object burst size of 2. */
+#elif RTE_GRAPH_BURST_SIZE == 4
+#define RTE_GRAPH_BURST_SIZE_LOG2 2  /**< Object burst size of 4. */
+#elif RTE_GRAPH_BURST_SIZE == 8
+#define RTE_GRAPH_BURST_SIZE_LOG2 3  /**< Object burst size of 8. */
+#elif RTE_GRAPH_BURST_SIZE == 16
+#define RTE_GRAPH_BURST_SIZE_LOG2 4  /**< Object burst size of 16. */
+#elif RTE_GRAPH_BURST_SIZE == 32
+#define RTE_GRAPH_BURST_SIZE_LOG2 5  /**< Object burst size of 32. */
+#elif RTE_GRAPH_BURST_SIZE == 64
+#define RTE_GRAPH_BURST_SIZE_LOG2 6  /**< Object burst size of 64. */
+#elif RTE_GRAPH_BURST_SIZE == 128
+#define RTE_GRAPH_BURST_SIZE_LOG2 7  /**< Object burst size of 128. */
+#elif RTE_GRAPH_BURST_SIZE == 256
+#define RTE_GRAPH_BURST_SIZE_LOG2 8  /**< Object burst size of 256. */
+#else
+#error "Unsupported burst size"
+#endif
+
+/* Forward declaration */
+struct rte_node;  /**< Node data */
+struct rte_graph; /**< Graph data */
+struct rte_graph_cluster_stats;      /**< Stats for Cluster of graphs */
+struct rte_graph_cluster_node_stats; /**< Node stats within cluster of graphs */
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node process function.
+ *
+ * The function invoked when the worker thread walks on nodes using
+ * rte_graph_walk().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param objs
+ *   Pointer to an array of objects to be processed.
+ * @param nb_objs
+ *   Number of objects in the array.
+ *
+ * @return
+ *   Number of objects processed.
+ *
+ * @see rte_graph_walk()
+ *
+ */
+typedef uint16_t (*rte_node_process_t)(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node initialization function.
+ *
+ * The function invoked when the user creates the graph using rte_graph_create()
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ *
+ * @see rte_graph_create()
+ */
+typedef int (*rte_node_init_t)(const struct rte_graph *graph,
+			       struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node finalization function.
+ *
+ * The function invoked when the user destroys the graph using
+ * rte_graph_destroy().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @see rte_graph_destroy()
+ */
+typedef void (*rte_node_fini_t)(const struct rte_graph *graph,
+				struct rte_node *node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph cluster stats callback.
+ *
+ * @param is_first
+ *   Flag to denote that stats are of the first node.
+ * @param is_last
+ *   Flag to denote that stats are of the last node.
+ * @param cookie
+ *   Cookie supplied during stats creation.
+ * @param stats
+ *   Node cluster stats data.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ */
+typedef int (*rte_graph_cluster_stats_cb_t)(bool is_first, bool is_last,
+	     void *cookie, const struct rte_graph_cluster_node_stats *stats);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for creating the graph.
+ *
+ * @see rte_graph_create()
+ */
+struct rte_graph_param {
+	int socket_id; /**< Socket id where memory is allocated. */
+	uint16_t nb_node_patterns;  /**< Number of node patterns. */
+	const char **node_patterns;
+	/**< Array of node patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure to hold configuration parameters for graph cluster stats create.
+ *
+ * @see rte_graph_cluster_stats_create()
+ */
+struct rte_graph_cluster_stats_param {
+	int socket_id;
+	/**< Socket id where memory is allocated */
+	rte_graph_cluster_stats_cb_t fn;
+	/**< Stats print callback function. NULL value allowed, in that case,
+	 *   default print stat function used.
+	 */
+	RTE_STD_C11
+	union {
+		void *cookie;
+		FILE *f; /**< File pointer to dump the stats when fn == NULL. */
+	};
+	uint16_t nb_graph_patterns;  /**< Number of graph patterns. */
+	const char **graph_patterns;
+	/**< Array of graph patterns based on shell pattern. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node cluster stats data structure.
+ *
+ * @see struct rte_graph_cluster_stats_param::fn
+ */
+struct rte_graph_cluster_node_stats {
+	uint64_t ts;	    /**< Current timestamp. */
+	uint64_t calls;	    /**< Current number of calls made. */
+	uint64_t objs;      /**< Current number of objs processed. */
+	uint64_t cycles;    /**< Current number of cycles. */
+
+	uint64_t prev_ts;	/**< Previous call timestamp. */
+	uint64_t prev_calls;	/**< Previous number of calls. */
+	uint64_t prev_objs;	/**< Previous number of processed objs. */
+	uint64_t prev_cycles;	/**< Previous number of cycles. */
+
+	uint64_t realloc_count; /**< Realloc count. */
+
+	rte_node_t id;	/**< Node identifier of stats. */
+	uint64_t hz;	/**< Cycles per seconds. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+} __rte_cache_aligned;
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Structure defines the node registration parameters.
+ *
+ * @see __rte_node_register(), RTE_NODE_REGISTER()
+ */
+struct rte_node_register {
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
+	rte_node_process_t process; /**< Node process function. */
+	rte_node_init_t init;       /**< Node init function. */
+	rte_node_fini_t fini;       /**< Node fini function. */
+	rte_node_t id;		    /**< Node Identifier. */
+	rte_node_t parent_id;       /**< Identifier of parent node. */
+	rte_edge_t nb_edges;        /**< Number of edges from this node. */
+	const char *next_nodes[];   /**< Names of next nodes. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create Graph.
+ *
+ * Create memory reel, detect loops and find isolated nodes.
+ *
+ * @param name
+ *   Unique name for this graph.
+ * @param prm
+ *   Graph parameter, includes node names and count to be included
+ *   in this graph.
+ *
+ * @return
+ *   Unique graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy Graph.
+ *
+ * Free Graph memory reel.
+ *
+ * @param name
+ *   Name of the graph to destroy.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_destroy(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph id from graph name.
+ *
+ * @param name
+ *   Name of the graph to get id.
+ *
+ * @return
+ *   Graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph name from graph id.
+ *
+ * @param id
+ *   id of the graph to get name.
+ *
+ * @return
+ *   Graph name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_graph_id_to_name(rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Export the graph as graph viz dot file
+ *
+ * @param name
+ *   Name of the graph to export.
+ * @param f
+ *   File pointer to export the graph.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_export(const char *name, FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get graph object from its name.
+ *
+ * Typical usage of this API to get graph objects in the worker thread and
+ * followed calling rte_graph_walk() in a loop.
+ *
+ * @param name
+ *   Name of the graph.
+ *
+ * @return
+ *   Graph pointer on success, NULL otherwise.
+ *
+ * @see rte_graph_walk()
+ */
+__rte_experimental
+struct rte_graph *rte_graph_lookup(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum number of graph available.
+ *
+ * @return
+ *   Maximum graph count on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump the graph information to file.
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param id
+ *   Graph id to get graph info.
+ */
+__rte_experimental
+void rte_graph_dump(FILE *f, rte_graph_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all graphs information to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ */
+__rte_experimental
+void rte_graph_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump graph information along with node info to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param graph
+ *   Graph pointer to get graph info.
+ * @param all
+ *   true to dump nodes in the graph.
+ */
+__rte_experimental
+void rte_graph_obj_dump(FILE *f, struct rte_graph *graph, bool all);
+
+/** Macro to browse rte_node object after the graph creation */
+#define rte_graph_foreach_node(count, off, graph, node)                        \
+	for (count = 0, off = graph->nodes_start,                              \
+	     node = RTE_PTR_ADD(graph, off);                                   \
+	     count < graph->nb_nodes;                                          \
+	     off = node->next, node = RTE_PTR_ADD(graph, off), count++)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node object with in graph from id.
+ *
+ * @param graph_id
+ *   Graph id to get node pointer from.
+ * @param node_id
+ *   Node id to get node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get(rte_graph_t graph_id, uint32_t node_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node pointer with in graph from name.
+ *
+ * @param graph
+ *   Graph name to get node pointer from.
+ * @param name
+ *   Node name to get the node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get_by_name(const char *graph,
+					    const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Create graph stats cluster to aggregate runtime node stats.
+ *
+ * @param prm
+ *   Parameters including file pointer to dump stats,
+ *   Graph pattern to create cluster and callback function.
+ *
+ * @return
+ *   Valid pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_graph_cluster_stats *rte_graph_cluster_stats_create(
+			const struct rte_graph_cluster_stats_param *prm);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Destroy cluster stats.
+ *
+ * @param stat
+ *    Valid cluster pointer to destroy.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get stats to application.
+ *
+ * @param[out] stat
+ *   Cluster status.
+ * @param skip_cb
+ *   true to skip callback function invocation.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat,
+				 bool skip_cb);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Reset cluster stats to zero.
+ *
+ * @param stat
+ *   Valid cluster stats pointer.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Register new packet processing node. Nodes can be registered
+ * dynamically via this call or statically via the RTE_NODE_REGISTER
+ * macro.
+ *
+ * @param node
+ *   Valid node pointer with name, process function and next_nodes.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ *
+ * @see RTE_NODE_REGISTER()
+ */
+__rte_experimental
+rte_node_t __rte_node_register(const struct rte_node_register *node);
+
+/**
+ * Register a static node.
+ *
+ * The static node is registered through the constructor scheme, thereby, it can
+ * be used in a multi-process scenario.
+ *
+ * @param node
+ *   Valid node pointer with name, process function, and next_nodes.
+ */
+#define RTE_NODE_REGISTER(node)                                                \
+	RTE_INIT(rte_node_register_##node)                                     \
+	{                                                                      \
+		node.parent_id = RTE_NODE_ID_INVALID;                          \
+		node.id = __rte_node_register(&node);                          \
+	}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Clone a node from static node(node created from RTE_NODE_REGISTER).
+ *
+ * @param id
+ *   Static node id to clone from.
+ * @param name
+ *   Name of the new node. The library prepends the parent node name to the
+ * user-specified name. The final node name will be,
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_clone(rte_node_t id, const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node id from node name.
+ *
+ * @param name
+ *   Valid node name. In the case of the cloned node, the name will be
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_from_name(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get node name from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid node name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_node_id_to_name(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the number of edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_count(rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Update the edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ * @param from
+ *   Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
+ * in that case, it will be added to the end of the list.
+ * @param next_nodes
+ *   Name of the edges to update.
+ * @param nb_edges
+ *   Number of edges to update.
+ *
+ * @return
+ *   Valid edge count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
+				const char **next_nodes, uint16_t nb_edges);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Shrink the edges to a given size.
+ *
+ * @param id
+ *   Valid node id.
+ * @param size
+ *   New size to shrink the edges.
+ *
+ * @return
+ *   New size on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the edge names from a given node.
+ *
+ * @param id
+ *   Valid node id.
+ * @param[out] next_nodes
+ *   Buffer to copy the edge names. The NULL value is allowed in that case,
+ * the function returns the size of the array that needs to be allocated.
+ *
+ * @return
+ *   When next_nodes == NULL, it returns the size of the array else
+ *  number of item copied.
+ */
+__rte_experimental
+rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get maximum nodes created so far.
+ *
+ * @return
+ *   Maximum nodes count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_max_count(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ * @param id
+ *   Node id to get the info.
+ */
+__rte_experimental
+void rte_node_dump(FILE *f, rte_node_t id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump all node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ */
+__rte_experimental
+void rte_node_list_dump(FILE *f);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of node id.
+ *
+ * @param id
+ *   Node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_node_is_invalid(rte_node_t id)
+{
+	return (id == RTE_NODE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of edge id.
+ *
+ * @param id
+ *   Edge node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_edge_is_invalid(rte_edge_t id)
+{
+	return (id == RTE_EDGE_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test the validity of graph id.
+ *
+ * @param id
+ *   Graph id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_is_invalid(rte_graph_t id)
+{
+	return (id == RTE_GRAPH_ID_INVALID);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Test stats feature support.
+ *
+ * @return
+ *   1 if stats enabled, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_has_stats_feature(void)
+{
+#ifdef RTE_LIBRTE_GRAPH_STATS
+	return RTE_LIBRTE_GRAPH_STATS;
+#else
+	return 0;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_H_ */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
new file mode 100644
index 000000000..55ef35df5
--- /dev/null
+++ b/lib/librte_graph/rte_graph_version.map
@@ -0,0 +1,3 @@
+EXPERIMENTAL {
+
+};
diff --git a/lib/meson.build b/lib/meson.build
index 9c3cc55d5..c43d86bb9 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index d295ca0a5..b1195f09a 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -98,6 +98,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
+_LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 02/29] graph: implement node registration
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 01/29] graph: define the public API for graph support jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 03/29] graph: implement node operations jerinj
                         ` (28 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding rte_node_register() API implementation includes allocating
memory for node object, check for duplicate node name and
add the allocated node to STAILQ node_list for future use.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph.c               |  18 +++-
 lib/librte_graph/graph_private.h       |  75 ++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/node.c                | 113 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   4 +
 6 files changed, 211 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/node.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 26fe514f3..933d0ee49 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -14,6 +14,7 @@ LDLIBS += -lrte_eal
 EXPORT_MAP := rte_graph_version.map
 
 # all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a55bf443a..a9c124896 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,4 +2,20 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
-#include "rte_graph.h"
+#include <rte_spinlock.h>
+
+#include "graph_private.h"
+
+static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+
+void
+graph_spinlock_lock(void)
+{
+	rte_spinlock_lock(&graph_lock);
+}
+
+void
+graph_spinlock_unlock(void)
+{
+	rte_spinlock_unlock(&graph_lock);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
new file mode 100644
index 000000000..8b9ff5292
--- /dev/null
+++ b/lib/librte_graph/graph_private.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_PRIVATE_H_
+#define _RTE_GRAPH_PRIVATE_H_
+
+#include <inttypes.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_eal.h>
+
+#include "rte_graph.h"
+
+/**
+ * @internal
+ *
+ * Structure that holds node internal data.
+ */
+struct node {
+	STAILQ_ENTRY(node) next;      /**< Next node in the list. */
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+	rte_node_process_t process;   /**< Node process function. */
+	rte_node_init_t init;         /**< Node init function. */
+	rte_node_fini_t fini;	      /**< Node fini function. */
+	rte_node_t id;		      /**< Allocated identifier for the node. */
+	rte_node_t parent_id;	      /**< Parent node identifier. */
+	rte_edge_t nb_edges;	      /**< Number of edges from this node. */
+	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
+};
+
+/* Node functions */
+STAILQ_HEAD(node_head, node);
+
+/**
+ * @internal
+ *
+ * Get the head of the node list.
+ *
+ * @return
+ *   Pointer to the node head.
+ */
+struct node_head *node_list_head_get(void);
+
+/**
+ * @internal
+ *
+ * Get node pointer from node name.
+ *
+ * @param name
+ *   Pointer to character string containing the node name.
+ *
+ * @return
+ *   Pointer to the node.
+ */
+struct node *node_from_name(const char *name);
+
+/* Lock functions */
+/**
+ * @internal
+ *
+ * Take a lock on the graph internal spin lock.
+ */
+void graph_spinlock_lock(void);
+
+/**
+ * @internal
+ *
+ * Release a lock on the graph internal spin lock.
+ */
+void graph_spinlock_unlock(void);
+
+#endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 455cf2ba5..5754ac23b 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('graph.c')
+sources = files('node.c', 'graph.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
new file mode 100644
index 000000000..336cd1c94
--- /dev/null
+++ b/lib/librte_graph/node.c
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_string_fns.h>
+
+#include "graph_private.h"
+
+static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
+static rte_node_t node_id;
+
+/* Private functions */
+struct node_head *
+node_list_head_get(void)
+{
+	return &node_list;
+}
+
+struct node *
+node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static bool
+node_has_duplicate_entry(const char *name)
+{
+	struct node *node;
+
+	/* Is duplicate name registered */
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0) {
+			rte_errno = EEXIST;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Public functions */
+rte_node_t
+__rte_node_register(const struct rte_node_register *reg)
+{
+	struct node *node;
+	rte_edge_t i;
+	size_t sz;
+
+	graph_spinlock_lock();
+
+	/* Check sanity */
+	if (reg == NULL || reg->process == NULL) {
+		rte_errno = EINVAL;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(reg->name))
+		goto fail;
+
+	sz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);
+	node = calloc(1, sz);
+	if (node == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Initialize the node */
+	if (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {
+		rte_errno = E2BIG;
+		goto free;
+	}
+	node->flags = reg->flags;
+	node->process = reg->process;
+	node->init = reg->init;
+	node->fini = reg->fini;
+	node->nb_edges = reg->nb_edges;
+	node->parent_id = reg->parent_id;
+	for (i = 0; i < reg->nb_edges; i++) {
+		if (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto free;
+		}
+	}
+
+	node->id = node_id++;
+
+	/* Add the node at tail */
+	STAILQ_INSERT_TAIL(&node_list, node, next);
+	graph_spinlock_unlock();
+
+	return node->id;
+free:
+	free(node);
+fail:
+	graph_spinlock_unlock();
+	return RTE_NODE_ID_INVALID;
+}
+
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 55ef35df5..0884c09f1 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -1,3 +1,7 @@
 EXPERIMENTAL {
+	global:
 
+	__rte_node_register;
+
+	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 03/29] graph: implement node operations
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 01/29] graph: define the public API for graph support jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 02/29] graph: implement node registration jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-06 17:57         ` Andrzej Ostruszka
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines jerinj
                         ` (27 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding node-specific API implementation like cloning node, updating
edges for the node, shrinking edges of a node, retrieving edges of a
node.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph_private.h       |  10 +
 lib/librte_graph/node.c                | 271 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  10 +
 3 files changed, 291 insertions(+)

diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 8b9ff5292..7ed6d01b6 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+
+#define ID_CHECK(id, id_max)                                                   \
+	do {                                                                   \
+		if ((id) >= (id_max)) {                                        \
+			rte_errno = EINVAL;                                    \
+			goto fail;                                             \
+		}                                                              \
+	} while (0)
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 336cd1c94..d04a0fce0 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -17,6 +17,8 @@
 static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
 static rte_node_t node_id;
 
+#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)
+
 /* Private functions */
 struct node_head *
 node_list_head_get(void)
@@ -111,3 +113,272 @@ __rte_node_register(const struct rte_node_register *reg)
 	return RTE_NODE_ID_INVALID;
 }
 
+static int
+clone_name(struct rte_node_register *reg, struct node *node, const char *name)
+{
+	ssize_t sz, rc;
+
+#define SZ RTE_NODE_NAMESIZE
+	rc = rte_strscpy(reg->name, node->name, SZ);
+	if (rc < 0)
+		goto fail;
+	sz = rc;
+	rc = rte_strscpy(reg->name + sz, "-", RTE_MAX((int16_t)(SZ - sz), 0));
+	if (rc < 0)
+		goto fail;
+	sz += rc;
+	sz = rte_strscpy(reg->name + sz, name, RTE_MAX((int16_t)(SZ - sz), 0));
+	if (sz < 0)
+		goto fail;
+
+	return 0;
+fail:
+	rte_errno = E2BIG;
+	return -rte_errno;
+}
+
+static rte_node_t
+node_clone(struct node *node, const char *name)
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct rte_node_register *reg;
+	rte_edge_t i;
+
+	/* Don't allow to clone a node from a cloned node */
+	if (node->parent_id != RTE_NODE_ID_INVALID) {
+		rte_errno = EEXIST;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(name))
+		goto fail;
+
+	reg = calloc(1, sizeof(*reg) + (sizeof(char *) * node->nb_edges));
+	if (reg == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Clone the source node */
+	reg->flags = node->flags;
+	reg->process = node->process;
+	reg->init = node->init;
+	reg->fini = node->fini;
+	reg->nb_edges = node->nb_edges;
+	reg->parent_id = node->id;
+
+	for (i = 0; i < node->nb_edges; i++)
+		reg->next_nodes[i] = node->next_nodes[i];
+
+	/* Naming ceremony of the new node. name is node->name + "-" + name */
+	if (clone_name(reg, node, name))
+		goto free;
+
+	rc = __rte_node_register(reg);
+free:
+	free(reg);
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_clone(rte_node_t id, const char *name)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node_clone(node, name);
+
+fail:
+	return RTE_NODE_ID_INVALID;
+}
+
+rte_node_t
+rte_node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node->id;
+
+	return RTE_NODE_ID_INVALID;
+}
+
+char *
+rte_node_id_to_name(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->name;
+
+fail:
+	return NULL;
+}
+
+rte_edge_t
+rte_node_edge_count(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->nb_edges;
+fail:
+	return RTE_EDGE_ID_INVALID;
+}
+
+static rte_edge_t
+edge_update(struct node *node, struct node *prev, rte_edge_t from,
+	    const char **next_nodes, rte_edge_t nb_edges)
+{
+	rte_edge_t i, max_edges, count = 0;
+	struct node *new_node;
+	bool need_realloc;
+	size_t sz;
+
+	if (from == RTE_EDGE_ID_INVALID)
+		from = node->nb_edges;
+
+	/* Don't create hole in next_nodes[] list */
+	if (from > node->nb_edges) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Remove me from list */
+	STAILQ_REMOVE(&node_list, node, node, next);
+
+	/* Allocate the storage space for new node if required */
+	max_edges = from + nb_edges;
+	need_realloc = max_edges > node->nb_edges;
+	if (need_realloc) {
+		sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
+		new_node = realloc(node, sz);
+		if (new_node == NULL) {
+			rte_errno = ENOMEM;
+			goto restore;
+		} else {
+			node = new_node;
+		}
+	}
+
+	/* Update the new nodes name */
+	for (i = from; i < max_edges; i++, count++) {
+		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto restore;
+		}
+	}
+restore:
+	/* Update the linked list to point new node address in prev node */
+	if (prev)
+		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
+	else
+		STAILQ_INSERT_HEAD(&node_list, node, next);
+
+	if (need_realloc)
+		node->nb_edges += max_edges;
+
+fail:
+	return count;
+}
+
+rte_edge_t
+rte_node_edge_shrink(rte_node_t id, rte_edge_t size)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (node->nb_edges < size) {
+				rte_errno = E2BIG;
+				goto fail;
+			}
+			node->nb_edges = size;
+			rc = size;
+			break;
+		}
+	}
+
+fail:
+	graph_spinlock_unlock();
+	return rc;
+}
+
+rte_edge_t
+rte_node_edge_update(rte_node_t id, rte_edge_t from, const char **next_nodes,
+		     uint16_t nb_edges)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *n, *prev;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	prev = NULL;
+	STAILQ_FOREACH(n, &node_list, next) {
+		if (n->id == id) {
+			rc = edge_update(n, prev, from, next_nodes, nb_edges);
+			break;
+		}
+		prev = n;
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+static rte_node_t
+node_copy_edges(struct node *node, char *next_nodes[])
+{
+	rte_edge_t i;
+
+	for (i = 0; i < node->nb_edges; i++)
+		next_nodes[i] = node->next_nodes[i];
+
+	return i;
+}
+
+rte_node_t
+rte_node_edge_get(rte_node_t id, char *next_nodes[])
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (next_nodes == NULL)
+				rc = sizeof(char *) * node->nb_edges;
+			else
+				rc = node_copy_edges(node, next_nodes);
+			break;
+		}
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_max_count(void)
+{
+	return node_id;
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 0884c09f1..412386356 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,5 +3,15 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 
+	rte_node_clone;
+	rte_node_edge_count;
+	rte_node_edge_get;
+	rte_node_edge_shrink;
+	rte_node_edge_update;
+	rte_node_from_name;
+	rte_node_id_to_name;
+	rte_node_list_dump;
+	rte_node_max_count;
+
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 04/29] graph: implement node debug routines
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (2 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 03/29] graph: implement node operations jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-06 18:17         ` Andrzej Ostruszka
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers jerinj
                         ` (26 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding node debug API implementation support to dump
single or all the node objects to the given file.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |  1 +
 lib/librte_graph/graph_debug.c         | 25 ++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 12 ++++++++++
 lib/librte_graph/meson.build           |  2 +-
 lib/librte_graph/node.c                | 32 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 6 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_debug.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 933d0ee49..2a6d86933 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
new file mode 100644
index 000000000..75238e7ca
--- /dev/null
+++ b/lib/librte_graph/graph_debug.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_debug.h>
+
+#include "graph_private.h"
+
+void
+node_dump(FILE *f, struct node *n)
+{
+	rte_edge_t i;
+
+	fprintf(f, "node <%s>\n", n->name);
+	fprintf(f, "  id=%" PRIu32 "\n", n->id);
+	fprintf(f, "  flags=0x%" PRIx64 "\n", n->flags);
+	fprintf(f, "  addr=%p\n", n);
+	fprintf(f, "  process=%p\n", n->process);
+	fprintf(f, "  nb_edges=%d\n", n->nb_edges);
+
+	for (i = 0; i < n->nb_edges; i++)
+		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
+}
+
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7ed6d01b6..6db04cee7 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -82,4 +82,16 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/**
+ * @internal
+ *
+ * Dump internal node object data.
+ *
+ * @param f
+ *   FILE pointer to dump the info.
+ * @param g
+ *   Pointer to the internal node object.
+ */
+void node_dump(FILE *f, struct node *n);
+
 #endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 5754ac23b..01512182f 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c')
+sources = files('node.c', 'graph.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index d04a0fce0..8592c1221 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
 	return rc;
 }
 
+static void
+node_scan_dump(FILE *f, rte_node_t id, bool all)
+{
+	struct node *node;
+
+	RTE_ASSERT(f != NULL);
+	NODE_ID_CHECK(id);
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (all == true) {
+			node_dump(f, node);
+		} else if (node->id == id) {
+			node_dump(f, node);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_node_dump(FILE *f, rte_node_t id)
+{
+	node_scan_dump(f, id, false);
+}
+
+void
+rte_node_list_dump(FILE *f)
+{
+	node_scan_dump(f, 0, true);
+}
+
 rte_node_t
 rte_node_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 412386356..f2c2139c5 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	__rte_node_register;
 
 	rte_node_clone;
+	rte_node_dump;
 	rte_node_edge_count;
 	rte_node_edge_get;
 	rte_node_edge_shrink;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (3 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-07 12:16         ` Andrzej Ostruszka
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for graph reel jerinj
                         ` (25 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding internal graph API helpers support to check whether a graph has
isolated nodes and any node have a loop to itself and BFS
algorithm implementation etc.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile        |   1 +
 lib/librte_graph/graph.c         |   2 +-
 lib/librte_graph/graph_ops.c     | 169 ++++++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h | 173 +++++++++++++++++++++++++++++++
 lib/librte_graph/meson.build     |   2 +-
 5 files changed, 345 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_ops.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 2a6d86933..39ecb2652 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a9c124896..4c3f2fe7b 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -7,7 +7,7 @@
 #include "graph_private.h"
 
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
-
+int rte_graph_logtype;
 void
 graph_spinlock_lock(void)
 {
diff --git a/lib/librte_graph/graph_ops.c b/lib/librte_graph/graph_ops.c
new file mode 100644
index 000000000..335595311
--- /dev/null
+++ b/lib/librte_graph/graph_ops.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+
+#include "graph_private.h"
+
+/* Check whether a node has next_node to itself */
+static inline int
+node_has_loop_edge(struct node *node)
+{
+	rte_edge_t i;
+	char *name;
+	int rc = 0;
+
+	for (i = 0; i < node->nb_edges; i++) {
+		if (strncmp(node->name, node->next_nodes[i],
+			    RTE_NODE_NAMESIZE) == 0) {
+			name = node->name;
+			rc = 1;
+			SET_ERR_JMP(EINVAL, fail, "Node %s has loop to self",
+				    name);
+		}
+	}
+fail:
+	return rc;
+}
+
+int
+graph_node_has_loop_edge(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (node_has_loop_edge(graph_node->node))
+			return 1;
+
+	return 0;
+}
+
+rte_node_t
+graph_src_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t rc = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F)
+			rc++;
+
+	if (rc == 0)
+		SET_ERR_JMP(EINVAL, fail, "Graph needs at least a source node");
+fail:
+	return rc;
+}
+
+/* Check whether a node has next_node to a source node */
+int
+graph_node_has_edge_to_src_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *node;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			node = graph_node->adjacency_list[i]->node;
+			if (node->flags & RTE_NODE_SOURCE_F)
+				SET_ERR_JMP(
+					EEXIST, fail,
+					"Node %s points to the source node %s",
+					graph_node->node->name, node->name);
+		}
+	}
+
+	return 0;
+fail:
+	return 1;
+}
+
+rte_node_t
+graph_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t count = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		count++;
+
+	return count;
+}
+
+void
+graph_mark_nodes_as_not_visited(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		graph_node->visited = false;
+}
+
+int
+graph_bfs(struct graph *graph, struct graph_node *start)
+{
+	struct graph_node **queue, *v, *tmp;
+	uint16_t head = 0, tail = 0;
+	rte_edge_t i;
+	size_t sz;
+
+	sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
+	queue = malloc(sz);
+	if (queue == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
+			    sz);
+
+	/* BFS algorithm */
+	queue[tail++] = start;
+	start->visited = true;
+	while (head != tail) {
+		v = queue[head++];
+		for (i = 0; i < v->node->nb_edges; i++) {
+			tmp = v->adjacency_list[i];
+			if (tmp->visited == false) {
+				queue[tail++] = tmp;
+				tmp->visited = true;
+			}
+		}
+	}
+
+	free(queue);
+	return 0;
+
+fail:
+	return -rte_errno;
+}
+
+/* Check whether a node has connected path or parent node */
+int
+graph_has_isolated_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	graph_mark_nodes_as_not_visited(graph);
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			if (graph_node->node->nb_edges == 0)
+				SET_ERR_JMP(EINVAL, fail,
+					    "%s node needs minimum one edge",
+					    graph_node->node->name);
+			if (graph_bfs(graph, graph_node))
+				goto fail;
+		}
+	}
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->visited == false)
+			SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
+				    graph_node->node->name);
+
+	return 0;
+fail:
+	return 1;
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 6db04cee7..220a35e2a 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+extern int rte_graph_logtype;
+
+#define GRAPH_LOG(level, ...)                                                  \
+	rte_log(RTE_LOG_##level, rte_graph_logtype,                            \
+		RTE_FMT("GRAPH: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",    \
+			__func__, __LINE__, RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define graph_err(...) GRAPH_LOG(ERR, __VA_ARGS__)
+#define graph_info(...) GRAPH_LOG(INFO, __VA_ARGS__)
+#define graph_dbg(...) GRAPH_LOG(DEBUG, __VA_ARGS__)
 
 #define ID_CHECK(id, id_max)                                                   \
 	do {                                                                   \
@@ -22,6 +32,12 @@
 		}                                                              \
 	} while (0)
 
+#define SET_ERR_JMP(err, where, fmt, ...)                                      \
+	do {                                                                   \
+		graph_err(fmt, ##__VA_ARGS__);                                 \
+		rte_errno = err;                                               \
+		goto where;                                                    \
+	} while (0)
 
 /**
  * @internal
@@ -41,6 +57,52 @@ struct node {
 	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
 };
 
+/**
+ * @internal
+ *
+ * Structure that holds the graph node data.
+ */
+struct graph_node {
+	STAILQ_ENTRY(graph_node) next; /**< Next graph node in the list. */
+	struct node *node; /**< Pointer to internal node. */
+	bool visited;      /**< Flag used in BFS to mark node visited. */
+	struct graph_node *adjacency_list[]; /**< Adjacency list of the node. */
+};
+
+/**
+ * @internal
+ *
+ * Structure that holds graph internal data.
+ */
+struct graph {
+	STAILQ_ENTRY(graph) next;
+	/**< List of graphs. */
+	char name[RTE_GRAPH_NAMESIZE];
+	/**< Name of the graph. */
+	const struct rte_memzone *mz;
+	/**< Memzone to store graph data. */
+	rte_graph_off_t nodes_start;
+	/**< Node memory start offset in graph reel. */
+	rte_node_t src_node_count;
+	/**< Number of source nodes in a graph. */
+	struct rte_graph *graph;
+	/**< Pointer to graph data. */
+	rte_node_t node_count;
+	/**< Total number of nodes. */
+	uint32_t cir_start;
+	/**< Circular buffer start offset in graph reel. */
+	uint32_t cir_mask;
+	/**< Circular buffer mask for wrap around. */
+	rte_graph_t id;
+	/**< Graph identifier. */
+	size_t mem_sz;
+	/**< Memory size of the graph. */
+	int socket;
+	/**< Socket identifier where memory is allocated. */
+	STAILQ_HEAD(gnode_list, graph_node) node_list;
+	/**< Nodes in a graph. */
+};
+
 /* Node functions */
 STAILQ_HEAD(node_head, node);
 
@@ -67,6 +129,19 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);
 
+/* Graph list functions */
+STAILQ_HEAD(graph_head, graph);
+
+/**
+ * @internal
+ *
+ * Get the head of the graph list.
+ *
+ * @return
+ *   Pointer to the graph head.
+ */
+struct graph_head *graph_list_head_get(void);
+
 /* Lock functions */
 /**
  * @internal
@@ -82,6 +157,104 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/* Graph operations */
+/**
+ * @internal
+ *
+ * Run a BFS(Breadth First Search) on the graph marking all
+ * the graph nodes as visited.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ * @param start
+ *   Pointer to the starting graph node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough memory for BFS.
+ */
+int graph_bfs(struct graph *graph, struct graph_node *start);
+
+/**
+ * @internal
+ *
+ * Check if there is an isolated node in the given graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: No isolated node found.
+ *   - 1: Isolated node found.
+ */
+int graph_has_isolated_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Check whether a node in the graph has next_node to a source node.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to source node.
+ *   - 1: Node doesn't have an edge to source node.
+ */
+int graph_node_has_edge_to_src_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Checks whether node in the graph has a edge to itself i.e. forms a
+ * loop.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to itself.
+ *   - 1: Node doesn't have an edge to itself.
+ */
+int graph_node_has_loop_edge(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of source nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of source nodes.
+ */
+rte_node_t graph_src_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of total number of nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of nodes.
+ */
+rte_node_t graph_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Clear the visited flag of all the nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ */
+void graph_mark_nodes_as_not_visited(struct graph *graph);
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 01512182f..16e0625c1 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_debug.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for graph reel
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (4 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-08 17:30         ` Andrzej Ostruszka
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 07/29] graph: implement create and destroy APIs jerinj
                         ` (24 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding support to create and populate the memory for graph reel.
This includes reserving the memory in the memzone, populating the nodes,
Allocating memory for node-specific streams to hold objects.

Once it is populated the reel memory contains the following sections.

+---------------------+
|   Graph Header      |
+---------------------+
|   Fence             |
+---------------------+
|   Circular buffer   |
+---------------------+
|   Fence             |
+---------------------+
|   Node Object 0     |
+------------------- -+
|   Node Object 1     |
+------------------- -+
|   Node Object 2     |
+------------------- -+
|   Node Object n     |
+------------------- -+

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   2 +
 lib/librte_graph/graph.c               |  16 ++
 lib/librte_graph/graph_populate.c      | 234 +++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       |  64 +++++++
 lib/librte_graph/meson.build           |   4 +-
 lib/librte_graph/node.c                |   5 +
 lib/librte_graph/rte_graph_version.map |   1 +
 lib/librte_graph/rte_graph_worker.h    | 108 ++++++++++++
 8 files changed, 432 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/rte_graph_worker.h

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 39ecb2652..7bfd7d51f 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,8 +18,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph_worker.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 4c3f2fe7b..e1930b7d2 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,6 +2,7 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <rte_malloc.h>
 #include <rte_spinlock.h>
 
 #include "graph_private.h"
@@ -19,3 +20,18 @@ graph_spinlock_unlock(void)
 {
 	rte_spinlock_unlock(&graph_lock);
 }
+
+void __rte_noinline
+__rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
diff --git a/lib/librte_graph/graph_populate.c b/lib/librte_graph/graph_populate.c
new file mode 100644
index 000000000..093512efa
--- /dev/null
+++ b/lib/librte_graph/graph_populate.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "graph_private.h"
+
+static size_t
+graph_fp_mem_calc_size(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t val;
+	size_t sz;
+
+	/* Graph header */
+	sz = sizeof(struct rte_graph);
+	/* Source nodes list */
+	sz += sizeof(rte_graph_off_t) * graph->src_node_count;
+	/* Circular buffer for pending streams of size number of nodes */
+	val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
+	sz = RTE_ALIGN(sz, val);
+	graph->cir_start = sz;
+	graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
+	sz += val;
+	/* Fence */
+	sz += sizeof(RTE_GRAPH_FENCE);
+	sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+	graph->nodes_start = sz;
+	/* For 0..N node objects with fence */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+		sz += sizeof(struct rte_node);
+		/* Pointer to next nodes(edges) */
+		sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
+	}
+
+	graph->mem_sz = sz;
+	return sz;
+}
+
+static void
+graph_header_popluate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+
+	graph->tail = 0;
+	graph->head = (int32_t)-_graph->src_node_count;
+	graph->cir_mask = _graph->cir_mask;
+	graph->nb_nodes = _graph->node_count;
+	graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
+	graph->nodes_start = _graph->nodes_start;
+	graph->socket = _graph->socket;
+	graph->id = _graph->id;
+	memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);
+	graph->fence = RTE_GRAPH_FENCE;
+}
+
+static void
+graph_nodes_populate(struct graph *_graph)
+{
+	rte_graph_off_t off = _graph->nodes_start;
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	rte_edge_t count, nb_edges;
+	const char *parent;
+	rte_node_t pid;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		struct rte_node *node = RTE_PTR_ADD(graph, off);
+		memset(node, 0, sizeof(*node));
+		node->fence = RTE_GRAPH_FENCE;
+		node->off = off;
+		node->process = graph_node->node->process;
+		memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
+		pid = graph_node->node->parent_id;
+		if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
+			parent = rte_node_id_to_name(pid);
+			memcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);
+		}
+		node->id = graph_node->node->id;
+		node->parent_id = pid;
+		nb_edges = graph_node->node->nb_edges;
+		node->nb_edges = nb_edges;
+		off += sizeof(struct rte_node);
+		/* Copy the name in first pass to replace with rte_node* later*/
+		for (count = 0; count < nb_edges; count++)
+			node->nodes[count] = (struct rte_node *)&graph_node
+						     ->adjacency_list[count]
+						     ->node->name[0];
+
+		off += sizeof(struct rte_node *) * nb_edges;
+		off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
+		node->next = off;
+		__rte_node_stream_alloc(graph, node);
+	}
+}
+
+struct rte_node *
+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (unlikely(node->id == id))
+			return node;
+
+	return NULL;
+}
+
+struct rte_node *
+graph_node_name_to_ptr(const struct rte_graph *graph, const char *name)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (strncmp(name, node->name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static int
+graph_node_nexts_populate(struct graph *_graph)
+{
+	rte_node_t count, val;
+	rte_graph_off_t off;
+	struct rte_node *node;
+	const struct rte_graph *graph = _graph->graph;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		for (val = 0; val < node->nb_edges; val++) {
+			name = (const char *)node->nodes[val];
+			node->nodes[val] = graph_node_name_to_ptr(graph, name);
+			if (node->nodes[val] == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_src_nodes_populate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	struct rte_node *node;
+	int32_t head = -1;
+	const char *name;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			name = graph_node->node->name;
+			node = graph_node_name_to_ptr(graph, name);
+			if (node == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+
+			__rte_node_stream_alloc(graph, node);
+			graph->cir_start[head--] = node->off;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_fp_mem_populate(struct graph *graph)
+{
+	int rc;
+
+	graph_header_popluate(graph);
+	graph_nodes_populate(graph);
+	rc = graph_node_nexts_populate(graph);
+	rc |= graph_src_nodes_populate(graph);
+
+	return rc;
+}
+
+int
+graph_fp_mem_create(struct graph *graph)
+{
+	const struct rte_memzone *mz;
+	size_t sz;
+
+	sz = graph_fp_mem_calc_size(graph);
+	mz = rte_memzone_reserve(graph->name, sz, graph->socket, 0);
+	if (mz == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Memzone %s reserve failed",
+			    graph->name);
+
+	graph->graph = mz->addr;
+	graph->mz = mz;
+
+	return graph_fp_mem_populate(graph);
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_nodes_mem_destroy(struct rte_graph *graph)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	if (graph == NULL)
+		return;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		rte_free(node->objs);
+}
+
+int
+graph_fp_mem_destroy(struct graph *graph)
+{
+	graph_nodes_mem_destroy(graph->graph);
+	return rte_memzone_free(graph->mz);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 220a35e2a..7fce52e00 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -12,6 +12,7 @@
 #include <rte_eal.h>
 
 #include "rte_graph.h"
+#include "rte_graph_worker.h"
 
 extern int rte_graph_logtype;
 
@@ -254,6 +255,69 @@ rte_node_t graph_nodes_count(struct graph *graph);
  */
 void graph_mark_nodes_as_not_visited(struct graph *graph);
 
+/* Fast path graph memory populate unctions */
+
+/**
+ * @internal
+ *
+ * Create fast-path memory for the graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough for graph and nodes.
+ *   - -EINVAL: Graph nodes not found.
+ */
+int graph_fp_mem_create(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Free fast-path memory used by graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Graph memzone related error.
+ */
+int graph_fp_mem_destroy(struct graph *graph);
+
+/* Lookup functions */
+/**
+ * @internal
+ *
+ * Get graph node object from node id.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param id
+ *   Node Identifier.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
+				      rte_node_t id);
+
+/**
+ * @internal
+ *
+ * Get graph node object from node name.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param node_name
+ *   Pointer to character string holding the node name.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
+					const char *node_name);
 
 /**
  * @internal
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 16e0625c1..fb203a5e2 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,8 +4,8 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
-headers = files('rte_graph.h')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
 deps += ['eal']
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 8592c1221..e05c4d5ed 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -61,6 +61,11 @@ __rte_node_register(const struct rte_node_register *reg)
 	rte_edge_t i;
 	size_t sz;
 
+	/* Limit Node specific metadata to one cacheline on 64B CL machine */
+	RTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -
+			  offsetof(struct rte_node, ctx)) !=
+			 RTE_CACHE_LINE_MIN_SIZE);
+
 	graph_spinlock_lock();
 
 	/* Check sanity */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index f2c2139c5..a9fe1b610 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -2,6 +2,7 @@ EXPERIMENTAL {
 	global:
 
 	__rte_node_register;
+	__rte_node_stream_alloc;
 
 	rte_node_clone;
 	rte_node_dump;
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
new file mode 100644
index 000000000..a8133739d
--- /dev/null
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_WORKER_H_
+#define _RTE_GRAPH_WORKER_H_
+
+/**
+ * @file rte_graph_worker.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows a worker thread to walk over a graph and nodes to create,
+ * process, enqueue and move streams of objects to the next nodes.
+ */
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+
+#include "rte_graph.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ *
+ * Data structure to hold graph data.
+ */
+struct rte_graph {
+	uint32_t tail;		     /**< Tail of circular buffer. */
+	uint32_t head;		     /**< Head of circular buffer. */
+	uint32_t cir_mask;	     /**< Circular buffer wrap around mask. */
+	rte_node_t nb_nodes;	     /**< Number of nodes in the graph. */
+	rte_graph_off_t *cir_start;  /**< Pointer to circular buffer. */
+	rte_graph_off_t nodes_start; /**< Offset at which node memory starts. */
+	rte_graph_t id;	/**< Graph identifier. */
+	int socket;	/**< Socket ID where memory is allocated. */
+	char name[RTE_GRAPH_NAMESIZE];	/**< Name of the graph. */
+	uint64_t fence;			/**< Fence. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Data structure to hold node data.
+ */
+struct rte_node {
+	/* Slow path area  */
+	uint64_t fence;		/**< Fence. */
+	rte_graph_off_t next;	/**< Index to next node. */
+	rte_node_t id;		/**< Node identifier. */
+	rte_node_t parent_id;	/**< Parent Node identifier. */
+	rte_edge_t nb_edges;	/**< Number of edges from this node. */
+	uint32_t realloc_count;	/**< Number of times realloced. */
+
+	char parent[RTE_NODE_NAMESIZE];	/**< Parent node name. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+
+	/* Fast path area  */
+#define RTE_NODE_CTX_SZ 16
+	uint8_t ctx[RTE_NODE_CTX_SZ] __rte_cache_aligned; /**< Node Context. */
+	uint16_t size;		/**< Total number of objects available. */
+	uint16_t idx;		/**< Number of objects used. */
+	rte_graph_off_t off;	/**< Offset of node in the graph reel. */
+	uint64_t total_cycles;	/**< Cycles spent in this node. */
+	uint64_t total_calls;	/**< Calls done to this node. */
+	uint64_t total_objs;	/**< Objects processed by this node. */
+	RTE_STD_C11
+		union {
+			void **objs;	   /**< Array of object pointers. */
+			uint64_t objs_u64;
+		};
+	RTE_STD_C11
+		union {
+			rte_node_process_t process; /**< Process function. */
+			uint64_t process_u64;
+		};
+	struct rte_node *nodes[] __rte_cache_min_aligned; /**< Next nodes. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ */
+__rte_experimental
+void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_WORKER_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 07/29] graph: implement create and destroy APIs
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (5 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for graph reel jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-08 16:57         ` Andrzej Ostruszka
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 08/29] graph: implement graph operation APIs jerinj
                         ` (23 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding graph specific API implementations like graph create
and graph destroy. This detect loops in the graph,
check for isolated nodes and operation to verify the validity of
graph.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 320 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   2 +
 2 files changed, 322 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e1930b7d2..dc373231e 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,13 +2,33 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_errno.h>
 #include <rte_malloc.h>
+#include <rte_memzone.h>
 #include <rte_spinlock.h>
+#include <rte_string_fns.h>
 
 #include "graph_private.h"
 
+static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+static rte_graph_t graph_id;
 int rte_graph_logtype;
+
+#define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id)
+
+/* Private functions */
+struct graph_head *
+graph_list_head_get(void)
+{
+	return &graph_list;
+}
+
 void
 graph_spinlock_lock(void)
 {
@@ -21,6 +41,306 @@ graph_spinlock_unlock(void)
 	rte_spinlock_unlock(&graph_lock);
 }
 
+static int
+graph_node_add(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+	size_t sz;
+
+	/* Skip the duplicate nodes */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (strncmp(node->name, graph_node->node->name,
+			    RTE_NODE_NAMESIZE) == 0)
+			return 0;
+
+	/* Allocate new graph node object */
+	sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);
+	graph_node = calloc(1, sz);
+
+	if (graph_node == NULL)
+		SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object",
+			    node->name);
+
+	/* Initialize the graph node */
+	graph_node->node = node;
+
+	/* Add to graph node list */
+	STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);
+	return 0;
+
+free:
+	free(graph_node);
+	return -rte_errno;
+}
+
+static struct graph_node *
+node_to_graph_node(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node == node)
+			return graph_node;
+
+	SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name);
+fail:
+	return NULL;
+}
+
+static int
+graph_node_edges_add(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			if (graph_node_add(graph, adjacency))
+				goto fail;
+		}
+	}
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_adjacency_list_update(struct graph *graph)
+{
+	struct graph_node *graph_node, *tmp;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			tmp = node_to_graph_node(graph, adjacency);
+			if (tmp == NULL)
+				goto fail;
+			graph_node->adjacency_list[i] = tmp;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+expand_pattern_to_node(struct graph *graph, const char *pattern)
+{
+	struct node_head *node_head = node_list_head_get();
+	bool found = false;
+	struct node *node;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(node, node_head, next) {
+		if (fnmatch(pattern, node->name, 0) == 0) {
+			if (graph_node_add(graph, node))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_cleanup(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	while (!STAILQ_EMPTY(&graph->node_list)) {
+		graph_node = STAILQ_FIRST(&graph->node_list);
+		STAILQ_REMOVE_HEAD(&graph->node_list, next);
+		free(graph_node);
+	}
+}
+
+static int
+graph_node_init(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	const char *name;
+	int rc;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->init) {
+			name = graph_node->node->name;
+			rc = graph_node->node->init(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph, name));
+			if (rc)
+				SET_ERR_JMP(rc, err, "Node %s init() failed",
+					    name);
+		}
+	}
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+graph_node_fini(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->fini)
+			graph_node->node->fini(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph,
+						       graph_node->node->name));
+}
+
+rte_graph_t
+rte_graph_create(const char *name, struct rte_graph_param *prm)
+{
+	struct graph *graph;
+	const char *pattern;
+	uint16_t i;
+
+	graph_spinlock_lock();
+
+	/* Check arguments sanity */
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
+
+	if (name == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
+
+	/* Check for existence of duplicate graph */
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
+			SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
+				    name);
+
+	/* Create graph object */
+	graph = calloc(1, sizeof(*graph));
+	if (graph == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
+
+	/* Initialize the graph object */
+	STAILQ_INIT(&graph->node_list);
+	if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
+		SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
+
+	/* Expand node pattern and add the nodes to the graph */
+	for (i = 0; i < prm->nb_node_patterns; i++) {
+		pattern = prm->node_patterns[i];
+		if (expand_pattern_to_node(graph, pattern))
+			goto graph_cleanup;
+	}
+
+	/* Go over all the nodes edges and add them to the graph */
+	if (graph_node_edges_add(graph))
+		goto graph_cleanup;
+
+	/* Update adjacency list of all nodes in the graph */
+	if (graph_adjacency_list_update(graph))
+		goto graph_cleanup;
+
+	/* Make sure at least a source node present in the graph */
+	if (!graph_src_nodes_count(graph))
+		goto graph_cleanup;
+
+	/* Make sure no node is pointing to source node */
+	if (graph_node_has_edge_to_src_node(graph))
+		goto graph_cleanup;
+
+	/* Don't allow node has loop to self */
+	if (graph_node_has_loop_edge(graph))
+		goto graph_cleanup;
+
+	/* Do BFS from src nodes on the graph to find isolated nodes */
+	if (graph_has_isolated_node(graph))
+		goto graph_cleanup;
+
+	/* Initialize graph object */
+	graph->socket = prm->socket_id;
+	graph->src_node_count = graph_src_nodes_count(graph);
+	graph->node_count = graph_nodes_count(graph);
+	graph->id = graph_id;
+
+	/* Allocate the Graph fast path memory and populate the data */
+	if (graph_fp_mem_create(graph))
+		goto graph_cleanup;
+
+	/* Call init() of the all the nodes in the graph */
+	if (graph_node_init(graph))
+		goto graph_mem_destroy;
+
+	/* All good, Lets add the graph to the list */
+	graph_id++;
+	STAILQ_INSERT_TAIL(&graph_list, graph, next);
+
+	graph_spinlock_unlock();
+	return graph->id;
+
+graph_mem_destroy:
+	graph_fp_mem_destroy(graph);
+graph_cleanup:
+	graph_cleanup(graph);
+free:
+	free(graph);
+fail:
+	graph_spinlock_unlock();
+	return RTE_GRAPH_ID_INVALID;
+}
+
+rte_graph_t
+rte_graph_destroy(const char *graph_name)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph, *tmp;
+	const char *name;
+
+	graph_spinlock_lock();
+
+	graph = STAILQ_FIRST(&graph_list);
+	while (graph != NULL) {
+		tmp = STAILQ_NEXT(graph, next);
+		name = graph->name;
+		if (strncmp(name, graph_name, RTE_GRAPH_NAMESIZE) == 0) {
+			/* Call fini() of the all the nodes in the graph */
+			graph_node_fini(graph);
+			/* Destroy graph fast path memory */
+			rc = graph_fp_mem_destroy(graph);
+			if (rc)
+				SET_ERR_JMP(rc, done, "MZ %s free failed",
+					    name);
+
+			graph_cleanup(graph);
+			STAILQ_REMOVE(&graph_list, graph, graph, next);
+			rc = graph->id;
+			free(graph);
+			graph_id--;
+			goto done;
+		}
+		graph = tmp;
+	}
+done:
+	graph_spinlock_unlock();
+	return rc;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index a9fe1b610..dcbd78c02 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,8 @@ EXPERIMENTAL {
 	__rte_node_register;
 	__rte_node_stream_alloc;
 
+	rte_graph_create;
+	rte_graph_destroy;
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 08/29] graph: implement graph operation APIs
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (6 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 07/29] graph: implement create and destroy APIs jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-08 17:49         ` Andrzej Ostruszka
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 09/29] graph: implement Graphviz export jerinj
                         ` (22 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K, Anatoly Burakov
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding support for graph specific API implementation like
Graph lookup to get graph object, retrieving graph ID
From name and graph name from ID.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 131 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   8 ++
 2 files changed, 139 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index dc373231e..7c6a7897d 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -210,6 +210,54 @@ graph_node_fini(struct graph *graph)
 						       graph_node->node->name));
 }
 
+static struct rte_graph *
+graph_mem_fixup_node_ctx(struct rte_graph *graph)
+{
+	struct rte_node *node;
+	struct node *node_db;
+	rte_graph_off_t off;
+	rte_node_t count;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
+			name = node->name;
+		else /* Cloned node */
+			name = node->parent;
+
+		node_db = node_from_name(name);
+		if (node_db == NULL)
+			SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
+		node->process = node_db->process;
+	}
+
+	return graph;
+fail:
+	return NULL;
+}
+
+static struct rte_graph *
+graph_mem_fixup_secondray(struct rte_graph *graph)
+{
+	if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
+		return graph;
+
+	return graph_mem_fixup_node_ctx(graph);
+}
+
+struct rte_graph *
+rte_graph_lookup(const char *name)
+{
+	const struct rte_memzone *mz;
+	struct rte_graph *rc = NULL;
+
+	mz = rte_memzone_lookup(name);
+	if (mz)
+		rc = mz->addr;
+
+	return graph_mem_fixup_secondray(rc);
+}
+
 rte_graph_t
 rte_graph_create(const char *name, struct rte_graph_param *prm)
 {
@@ -341,6 +389,76 @@ rte_graph_destroy(const char *graph_name)
 	return rc;
 }
 
+rte_graph_t
+rte_graph_from_name(const char *name)
+{
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)
+			return graph->id;
+
+	return RTE_GRAPH_ID_INVALID;
+}
+
+char *
+rte_graph_id_to_name(rte_graph_t id)
+{
+	struct graph *graph;
+
+	GRAPH_ID_CHECK(id);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == id)
+			return graph->name;
+
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get(rte_graph_t gid, uint32_t nid)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	GRAPH_ID_CHECK(gid);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == gid) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (node->id == nid)
+					return node;
+			}
+			break;
+		}
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get_by_name(const char *graph_name, const char *node_name)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (!strncmp(node->name, node_name,
+					     RTE_NODE_NAMESIZE))
+					return node;
+			}
+			break;
+		}
+
+	return NULL;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
@@ -355,3 +473,16 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->size = size;
 	node->realloc_count++;
 }
+
+rte_graph_t
+rte_graph_max_count(void)
+{
+	return graph_id;
+}
+
+RTE_INIT(rte_graph_init_log)
+{
+	rte_graph_logtype = rte_log_register("lib.graph");
+	if (rte_graph_logtype >= 0)
+		rte_log_set_level(rte_graph_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index dcbd78c02..5a2b13293 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,14 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_from_name;
+	rte_graph_id_to_name;
+	rte_graph_lookup;
+	rte_graph_list_dump;
+	rte_graph_max_count;
+	rte_graph_node_get;
+	rte_graph_node_get_by_name;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 09/29] graph: implement Graphviz export
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (7 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 08/29] graph: implement graph operation APIs jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 10/29] graph: implement debug routines jerinj
                         ` (21 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding API implementation support exporting the graph object to file.
This will export the graph to a file in Graphviz format.
It can be viewed in many viewers such as
https://dreampuf.github.io/GraphvizOnline/

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 54 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 2 files changed, 55 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 7c6a7897d..e0c0b71a7 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -474,6 +474,60 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+static int
+graph_to_dot(FILE *f, struct graph *graph)
+{
+	const char *src_edge_color = " [color=blue]\n";
+	const char *edge_color = "\n";
+	struct graph_node *graph_node;
+	char *node_name;
+	rte_edge_t i;
+	int rc;
+
+	rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name);
+	if (rc < 0)
+		goto end;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		node_name = graph_node->node->name;
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name,
+				     graph_node->adjacency_list[i]->node->name,
+				     graph_node->node->flags & RTE_NODE_SOURCE_F
+					     ? src_edge_color
+					     : edge_color);
+			if (rc < 0)
+				goto end;
+		}
+	}
+	rc = fprintf(f, "}\n");
+	if (rc < 0)
+		goto end;
+
+	return 0;
+end:
+	rte_errno = EBADF;
+	return -rte_errno;
+}
+
+rte_graph_t
+rte_graph_export(const char *name, FILE *f)
+{
+	rte_graph_t rc = RTE_GRAPH_ID_INVALID;
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {
+			rc = graph_to_dot(f, graph);
+			goto end;
+		}
+	}
+	rte_errno = ENOENT;
+end:
+	return rc;
+}
+
+
 rte_graph_t
 rte_graph_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 5a2b13293..2797be044 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
 	rte_graph_lookup;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 10/29] graph: implement debug routines
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (8 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 09/29] graph: implement Graphviz export jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 11/29] graph: implement stats support jerinj
                         ` (20 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph specific API to dump the
Graph information to a file. This API will dump detailed internal
info about node objects and graph objects.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 31 ++++++++++++++
 lib/librte_graph/graph_debug.c         | 59 ++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 13 ++++++
 lib/librte_graph/rte_graph_version.map |  2 +
 4 files changed, 105 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e0c0b71a7..e96363777 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -527,6 +527,37 @@ rte_graph_export(const char *name, FILE *f)
 	return rc;
 }
 
+static void
+graph_scan_dump(FILE *f, rte_graph_t id, bool all)
+{
+	struct graph *graph;
+
+	RTE_VERIFY(f);
+	GRAPH_ID_CHECK(id);
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (all == true) {
+			graph_dump(f, graph);
+		} else if (graph->id == id) {
+			graph_dump(f, graph);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_graph_dump(FILE *f, rte_graph_t id)
+{
+	graph_scan_dump(f, id, false);
+}
+
+void
+rte_graph_list_dump(FILE *f)
+{
+	graph_scan_dump(f, 0, true);
+}
 
 rte_graph_t
 rte_graph_max_count(void)
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
index 75238e7ca..f8aea16ac 100644
--- a/lib/librte_graph/graph_debug.c
+++ b/lib/librte_graph/graph_debug.c
@@ -7,6 +7,26 @@
 
 #include "graph_private.h"
 
+void
+graph_dump(FILE *f, struct graph *g)
+{
+	struct graph_node *graph_node;
+	rte_edge_t i = 0;
+
+	fprintf(f, "graph <%s>\n", g->name);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  cir_start=%" PRIu32 "\n", g->cir_start);
+	fprintf(f, "  cir_mask=%" PRIu32 "\n", g->cir_mask);
+	fprintf(f, "  addr=%p\n", g);
+	fprintf(f, "  graph=%p\n", g->graph);
+	fprintf(f, "  mem_sz=%zu\n", g->mem_sz);
+	fprintf(f, "  node_count=%" PRIu32 "\n", g->node_count);
+	fprintf(f, "  src_node_count=%" PRIu32 "\n", g->src_node_count);
+
+	STAILQ_FOREACH(graph_node, &g->node_list, next)
+		fprintf(f, "     node[%d] <%s>\n", i++, graph_node->node->name);
+}
+
 void
 node_dump(FILE *f, struct node *n)
 {
@@ -23,3 +43,42 @@ node_dump(FILE *f, struct node *n)
 		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
 }
 
+void
+rte_graph_obj_dump(FILE *f, struct rte_graph *g, bool all)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *n;
+	rte_edge_t i;
+
+	fprintf(f, "graph <%s> @ %p\n", g->name, g);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  head=%" PRId32 "\n", (int32_t)g->head);
+	fprintf(f, "  tail=%" PRId32 "\n", (int32_t)g->tail);
+	fprintf(f, "  cir_mask=0x%" PRIx32 "\n", g->cir_mask);
+	fprintf(f, "  nb_nodes=%" PRId32 "\n", g->nb_nodes);
+	fprintf(f, "  socket=%d\n", g->socket);
+	fprintf(f, "  fence=0x%" PRIx64 "\n", g->fence);
+	fprintf(f, "  nodes_start=0x%" PRIx32 "\n", g->nodes_start);
+	fprintf(f, "  cir_start=%p\n", g->cir_start);
+
+	rte_graph_foreach_node(count, off, g, n) {
+		if (!all && n->idx == 0)
+			continue;
+		fprintf(f, "     node[%d] <%s>\n", count, n->name);
+		fprintf(f, "       fence=0x%" PRIx64 "\n", n->fence);
+		fprintf(f, "       objs=%p\n", n->objs);
+		fprintf(f, "       process=%p\n", n->process);
+		fprintf(f, "       id=0x%" PRIx32 "\n", n->id);
+		fprintf(f, "       offset=0x%" PRIx32 "\n", n->off);
+		fprintf(f, "       nb_edges=%" PRId32 "\n", n->nb_edges);
+		fprintf(f, "       realloc_count=%d\n", n->realloc_count);
+		fprintf(f, "       size=%d\n", n->size);
+		fprintf(f, "       idx=%d\n", n->idx);
+		fprintf(f, "       total_objs=%" PRId64 "\n", n->total_objs);
+		fprintf(f, "       total_calls=%" PRId64 "\n", n->total_calls);
+		for (i = 0; i < n->nb_edges; i++)
+			fprintf(f, "          edge[%d] <%s>\n", i,
+				n->nodes[i]->name);
+	}
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7fce52e00..f9a85c892 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -319,6 +319,19 @@ struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
 struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
 					const char *node_name);
 
+/* Debug functions */
+/**
+ * @internal
+ *
+ * Dump internal graph object data.
+ *
+ * @param f
+ *   FILE pointer to dump the data.
+ * @param g
+ *   Pointer to the internal graph object.
+ */
+void graph_dump(FILE *f, struct graph *g);
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 2797be044..851f4772e 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_dump;
 	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
@@ -14,6 +15,7 @@ EXPERIMENTAL {
 	rte_graph_max_count;
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
+	rte_graph_obj_dump;
 
 	rte_node_clone;
 	rte_node_dump;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 11/29] graph: implement stats support
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (9 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 10/29] graph: implement debug routines jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 12/29] graph: implement fastpath API routines jerinj
                         ` (19 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph stats collection API. This API will
create a cluster for a specified node pattern and aggregate the node
runtime stats.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph_stats.c         | 406 +++++++++++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/rte_graph_version.map |   5 +
 4 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_stats.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 7bfd7d51f..967c8d9bc 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,6 +18,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_stats.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
diff --git a/lib/librte_graph/graph_stats.c b/lib/librte_graph/graph_stats.c
new file mode 100644
index 000000000..125e08d73
--- /dev/null
+++ b/lib/librte_graph/graph_stats.c
@@ -0,0 +1,406 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+
+#include "graph_private.h"
+
+/* Capture all graphs of cluster */
+struct cluster {
+	rte_graph_t nb_graphs;
+	rte_graph_t size;
+
+	struct graph **graphs;
+};
+
+/* Capture same node ID across cluster  */
+struct cluster_node {
+	struct rte_graph_cluster_node_stats stat;
+	rte_node_t nb_nodes;
+
+	struct rte_node *nodes[];
+};
+
+struct rte_graph_cluster_stats {
+	/* Header */
+	rte_graph_cluster_stats_cb_t fn;
+	uint32_t cluster_node_size; /* Size of struct cluster_node */
+	rte_node_t max_nodes;
+	int socket_id;
+	void *cookie;
+	size_t sz;
+
+	struct cluster_node clusters[];
+} __rte_cache_aligned;
+
+#define boarder()                                                              \
+	fprintf(f, "+-------------------------------+---------------+--------" \
+		   "-------+---------------+---------------+---------------+-" \
+		   "----------+\n")
+
+static inline void
+print_banner(FILE *f)
+{
+	boarder();
+	fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
+		"|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
+		"|cycles/call|");
+	boarder();
+}
+
+static inline void
+print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
+{
+	double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
+	const uint64_t prev_calls = stat->prev_calls;
+	const uint64_t prev_objs = stat->prev_objs;
+	const uint64_t cycles = stat->cycles;
+	const uint64_t calls = stat->calls;
+	const uint64_t objs = stat->objs;
+	uint64_t call_delta;
+
+	call_delta = calls - prev_calls;
+	objs_per_call =
+		call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
+	cycles_per_call =
+		call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
+			   : 0;
+	ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
+	objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
+	objs_per_sec /= 1000000;
+
+	fprintf(f,
+		"|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
+		"|%-15.3f|%-15.6f|%-11.4f|\n",
+		stat->name, calls, objs, stat->realloc_count, objs_per_call,
+		objs_per_sec, cycles_per_call);
+}
+
+static int
+graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
+		       const struct rte_graph_cluster_node_stats *stat)
+{
+	FILE *f = cookie;
+
+	if (unlikely(is_first))
+		print_banner(f);
+	if (stat->objs)
+		print_node(f, stat);
+	if (unlikely(is_last))
+		boarder();
+
+	return 0;
+};
+
+static struct rte_graph_cluster_stats *
+stats_mem_init(struct cluster *cluster,
+	       const struct rte_graph_cluster_stats_param *prm)
+{
+	size_t sz = sizeof(struct rte_graph_cluster_stats);
+	struct rte_graph_cluster_stats *stats;
+	rte_graph_cluster_stats_cb_t fn;
+	int socket_id = prm->socket_id;
+	uint32_t cluster_node_size;
+
+	/* Fix up callback */
+	fn = prm->fn;
+	if (fn == NULL)
+		fn = graph_cluster_stats_cb;
+
+	cluster_node_size = sizeof(struct cluster_node);
+	/* For a given cluster, max nodes will be the max number of graphs */
+	cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
+	cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
+
+	stats = realloc(NULL, sz);
+	memset(stats, 0, sz);
+	if (stats) {
+		stats->fn = fn;
+		stats->cluster_node_size = cluster_node_size;
+		stats->max_nodes = 0;
+		stats->socket_id = socket_id;
+		stats->cookie = prm->cookie;
+		stats->sz = sz;
+	}
+
+	return stats;
+}
+
+static int
+stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
+		   struct rte_graph *graph, struct graph_node *graph_node)
+{
+	struct rte_graph_cluster_stats *stats = *stats_in;
+	rte_node_t id = graph_node->node->id;
+	struct cluster_node *cluster;
+	struct rte_node *node;
+	rte_node_t count;
+
+	cluster = stats->clusters;
+
+	/* Iterate over cluster node array to find node ID match */
+	for (count = 0; count < stats->max_nodes; count++) {
+		/* Found an existing node in the reel */
+		if (cluster->stat.id == id) {
+			node = graph_node_id_to_ptr(graph, id);
+			if (node == NULL)
+				SET_ERR_JMP(
+					ENOENT, err,
+					"Failed to find node %s in graph %s",
+					graph_node->node->name, graph->name);
+
+			cluster->nodes[cluster->nb_nodes++] = node;
+			return 0;
+		}
+		cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
+	}
+
+	/* Hey, it is a new node, allocate space for it in the reel */
+	stats = realloc(stats, stats->sz + stats->cluster_node_size);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, err, "Realloc failed");
+
+	/* Clear the new struct cluster_node area */
+	cluster = RTE_PTR_ADD(stats, stats->sz),
+	memset(cluster, 0, stats->cluster_node_size);
+	memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
+	cluster->stat.id = graph_node->node->id;
+	cluster->stat.hz = rte_get_timer_hz();
+	node = graph_node_id_to_ptr(graph, id);
+	if (node == NULL)
+		SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s",
+			    graph_node->node->name, graph->name);
+	cluster->nodes[cluster->nb_nodes++] = node;
+
+	stats->sz += stats->cluster_node_size;
+	stats->max_nodes++;
+	*stats_in = stats;
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+stats_mem_fini(struct rte_graph_cluster_stats *stats)
+{
+	free(stats);
+}
+
+static void
+cluster_init(struct cluster *cluster)
+{
+	memset(cluster, 0, sizeof(*cluster));
+}
+
+static int
+cluster_add(struct cluster *cluster, struct graph *graph)
+{
+	rte_graph_t count;
+	size_t sz;
+
+	/* Skip the if graph is already added to cluster */
+	for (count = 0; count < cluster->nb_graphs; count++)
+		if (cluster->graphs[count] == graph)
+			return 0;
+
+	/* Expand the cluster if required to store graph objects */
+	if (cluster->nb_graphs + 1 > cluster->size) {
+		cluster->size = RTE_MAX(1, cluster->size * 2);
+		sz = sizeof(struct graph *) * cluster->size;
+		cluster->graphs = realloc(cluster->graphs, sz);
+		if (cluster->graphs == NULL)
+			SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
+	}
+
+	/* Add graph to cluster */
+	cluster->graphs[cluster->nb_graphs++] = graph;
+	return 0;
+
+free:
+	return -rte_errno;
+}
+
+static void
+cluster_fini(struct cluster *cluster)
+{
+	if (cluster->graphs)
+		free(cluster->graphs);
+}
+
+static int
+expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
+{
+	struct graph_head *graph_head = graph_list_head_get();
+	struct graph *graph;
+	bool found = false;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(graph, graph_head, next) {
+		if (fnmatch(pattern, graph->name, 0) == 0) {
+			if (cluster_add(cluster, graph))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
+			    pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+struct rte_graph_cluster_stats *
+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
+{
+	struct rte_graph_cluster_stats *stats, *rc = NULL;
+	struct graph_node *graph_node;
+	struct cluster cluster;
+	struct graph *graph;
+	const char *pattern;
+	rte_graph_t i;
+
+	/* Sanity checks */
+	if (!rte_graph_has_stats_feature())
+		SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
+
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Invalid param");
+
+	if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
+		SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
+
+	cluster_init(&cluster);
+
+	graph_spinlock_lock();
+	/* Expand graph pattern and add the graph to the cluster */
+	for (i = 0; i < prm->nb_graph_patterns; i++) {
+		pattern = prm->graph_patterns[i];
+		if (expand_pattern_to_cluster(&cluster, pattern))
+			goto bad_pattern;
+	}
+
+	/* Alloc the stats memory */
+	stats = stats_mem_init(&cluster, prm);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
+
+	/* Iterate over M(Graph) x N (Nodes in graph) */
+	for (i = 0; i < cluster.nb_graphs; i++) {
+		graph = cluster.graphs[i];
+		STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+			struct rte_graph *graph_fp = graph->graph;
+			if (stats_mem_populate(&stats, graph_fp, graph_node))
+				goto realloc_fail;
+		}
+	}
+
+	/* Finally copy to hugepage memory to avoid pressure on rte_realloc */
+	rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
+	if (rc)
+		rte_memcpy(rc, stats, stats->sz);
+	else
+		SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
+
+realloc_fail:
+	stats_mem_fini(stats);
+bad_pattern:
+	graph_spinlock_unlock();
+	cluster_fini(&cluster);
+fail:
+	return rc;
+}
+
+void
+rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
+{
+	return rte_free(stat);
+}
+
+static inline void
+cluster_node_arregate_stats(struct cluster_node *cluster)
+{
+	uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+	struct rte_node *node;
+	rte_node_t count;
+
+	for (count = 0; count < cluster->nb_nodes; count++) {
+		node = cluster->nodes[count];
+
+		calls += node->total_calls;
+		objs += node->total_objs;
+		cycles += node->total_cycles;
+		realloc_count += node->realloc_count;
+	}
+
+	stat->calls = calls;
+	stat->objs = objs;
+	stat->cycles = cycles;
+	stat->ts = rte_get_timer_cycles();
+	stat->realloc_count = realloc_count;
+}
+
+static inline void
+cluster_node_store_prev_stats(struct cluster_node *cluster)
+{
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+
+	stat->prev_ts = stat->ts;
+	stat->prev_calls = stat->calls;
+	stat->prev_objs = stat->objs;
+	stat->prev_cycles = stat->cycles;
+}
+
+void
+rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+	int rc = 0;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		cluster_node_arregate_stats(cluster);
+		if (!skip_cb)
+			rc = stat->fn(!count, (count == stat->max_nodes - 1),
+				      stat->cookie, &cluster->stat);
+		cluster_node_store_prev_stats(cluster);
+		if (rc)
+			break;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
+
+void
+rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		struct rte_graph_cluster_node_stats *node = &cluster->stat;
+
+		node->ts = 0;
+		node->calls = 0;
+		node->objs = 0;
+		node->cycles = 0;
+		node->prev_ts = 0;
+		node->prev_calls = 0;
+		node->prev_objs = 0;
+		node->prev_cycles = 0;
+		node->realloc_count = 0;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index fb203a5e2..929a17f84 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_stats.c', 'graph_populate.c')
 headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 851f4772e..adf55d406 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -17,6 +17,11 @@ EXPERIMENTAL {
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
 
+	rte_graph_cluster_stats_create;
+	rte_graph_cluster_stats_destroy;
+	rte_graph_cluster_stats_get;
+	rte_graph_cluster_stats_reset;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 12/29] graph: implement fastpath API routines
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (10 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 11/29] graph: implement stats support jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-09 23:07         ` Andrzej Ostruszka
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 13/29] graph: add unit test case jerinj
                         ` (18 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for rte_graph_walk() API. This will perform a walk
on the circular buffer and call the process function of each node
and collect the stats if stats collection is enabled.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md              |   1 +
 lib/librte_graph/graph.c               |  16 +
 lib/librte_graph/rte_graph_version.map |  10 +
 lib/librte_graph/rte_graph_worker.h    | 434 +++++++++++++++++++++++++
 4 files changed, 461 insertions(+)

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5cc50f750..fd2ff64d7 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -160,6 +160,7 @@ The public API headers are grouped by topics:
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
+    [graph_worker]     (@ref rte_graph_worker.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e96363777..d5d816c71 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -474,6 +474,22 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+void __rte_noinline
+__rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,
+			     uint16_t req_size)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
+
 static int
 graph_to_dot(FILE *f, struct graph *graph)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index adf55d406..13b838752 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 	__rte_node_stream_alloc;
+	__rte_node_stream_alloc_size;
 
 	rte_graph_create;
 	rte_graph_destroy;
@@ -16,6 +17,7 @@ EXPERIMENTAL {
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
+	rte_graph_walk;
 
 	rte_graph_cluster_stats_create;
 	rte_graph_cluster_stats_destroy;
@@ -28,10 +30,18 @@ EXPERIMENTAL {
 	rte_node_edge_get;
 	rte_node_edge_shrink;
 	rte_node_edge_update;
+	rte_node_enqueue;
+	rte_node_enqueue_x1;
+	rte_node_enqueue_x2;
+	rte_node_enqueue_x4;
+	rte_node_enqueue_next;
 	rte_node_from_name;
 	rte_node_id_to_name;
 	rte_node_list_dump;
 	rte_node_max_count;
+	rte_node_next_stream_get;
+	rte_node_next_stream_put;
+	rte_node_next_stream_move;
 
 	local: *;
 };
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
index a8133739d..a1bfc498b 100644
--- a/lib/librte_graph/rte_graph_worker.h
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -101,6 +101,440 @@ struct rte_node {
 __rte_experimental
 void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
 
+/**
+ * @internal
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a stream with requested number of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param req_size
+ *   Number of objects to be allocated.
+ */
+__rte_experimental
+void __rte_node_stream_alloc_size(struct rte_graph *graph,
+				  struct rte_node *node, uint16_t req_size);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Perform graph walk on the circular buffer and invoke the process function
+ * of the nodes and collect the stats.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup function.
+ *
+ * @see rte_graph_lookup()
+ */
+__rte_experimental
+static inline void
+rte_graph_walk(struct rte_graph *graph)
+{
+	const rte_graph_off_t *cir_start = graph->cir_start;
+	const rte_node_t mask = graph->cir_mask;
+	uint32_t head = graph->head;
+	struct rte_node *node;
+	uint64_t start;
+	uint16_t rc;
+	void **objs;
+
+	/*
+	 * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
+	 * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
+	 * in a circular buffer fashion.
+	 *
+	 *	+-----+ <= cir_start - head [number of source nodes]
+	 *	|     |
+	 *	| ... | <= source nodes
+	 *	|     |
+	 *	+-----+ <= cir_start [head = 0] [tail = 0]
+	 *	|     |
+	 *	| ... | <= pending streams
+	 *	|     |
+	 *	+-----+ <= cir_start + mask
+	 */
+	while (likely(head != graph->tail)) {
+		node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
+		RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+		objs = node->objs;
+		rte_prefetch0(objs);
+
+		if (rte_graph_has_stats_feature()) {
+			start = rte_rdtsc();
+			rc = node->process(graph, node, objs, node->idx);
+			node->total_cycles += rte_rdtsc() - start;
+			node->total_calls++;
+			node->total_objs += rc;
+		} else {
+			node->process(graph, node, objs, node->idx);
+		}
+		node->idx = 0;
+		head = likely((int32_t)head > 0) ? head & mask : head;
+	}
+	graph->tail = 0;
+}
+
+/* Fast path helper functions */
+
+/**
+ * @internal
+ *
+ * Enqueue a given node to the tail of the graph reel.
+ *
+ * @param graph
+ *   Pointer Graph object.
+ * @param node
+ *   Pointer to node object to be enqueued.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_tail_update(struct rte_graph *graph, struct rte_node *node)
+{
+	uint32_t tail;
+
+	tail = graph->tail;
+	graph->cir_start[tail++] = node->off;
+	graph->tail = tail & graph->cir_mask;
+}
+
+/**
+ * @internal
+ *
+ * Enqueue sequence prologue function.
+ *
+ * Updates the node to tail of graph reel and resizes the number of objects
+ * available in the stream as needed.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param idx
+ *   Index at which the object enqueue starts from.
+ * @param space
+ *   Space required for the object enqueue.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_prologue(struct rte_graph *graph, struct rte_node *node,
+			    const uint16_t idx, const uint16_t space)
+{
+
+	/* Add to the pending stream list if the node is new */
+	if (idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	if (unlikely(node->size < (idx + space)))
+		__rte_node_stream_alloc(graph, node);
+}
+
+/**
+ * @internal
+ *
+ * Get the node pointer from current node edge id.
+ *
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Edge id of the required node.
+ *
+ * @return
+ *   Pointer to the node denoted by the edge id.
+ */
+static __rte_always_inline struct rte_node *
+__rte_node_next_node_get(struct rte_node *node, rte_edge_t next)
+{
+	RTE_ASSERT(next < node->nb_edges);
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+	node = node->nodes[next];
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+
+	return node;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue the objs to next node for further processing and set
+ * the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param objs
+ *   Objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
+		 rte_edge_t next, void **objs, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, nb_objs);
+
+	rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
+	node->idx = idx + nb_objs;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only one obj to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x1(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 1);
+
+	node->objs[idx++] = obj;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only two objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue two objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   Obj to enqueue.
+ * @param obj1
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x2(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 2);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue only four objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue four objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   1st obj to enqueue.
+ * @param obj1
+ *   2nd obj to enqueue.
+ * @param obj2
+ *   3rd obj to enqueue.
+ * @param obj3
+ *   4th obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x4(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1, void *obj2,
+		    void *obj3)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 4);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->objs[idx++] = obj2;
+	node->objs[idx++] = obj3;
+	node->idx = idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Enqueue objs to multiple next nodes for further processing and
+ * set the next nodes to pending state in the circular buffer.
+ * objs[i] will be enqueued to nexts[i].
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param nexts
+ *   List of relative next node indices to enqueue objs.
+ * @param objs
+ *   List of objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
+		      rte_edge_t *nexts, void **objs, uint16_t nb_objs)
+{
+	uint16_t i;
+
+	for (i = 0; i < nb_objs; i++)
+		rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the stream of next node to enqueue the objs.
+ * Once done with the updating the objs, needs to call
+ * rte_node_next_stream_put to put the next node to pending state.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to get stream.
+ * @param nb_objs
+ *   Requested free size of the next stream.
+ *
+ * @return
+ *   Valid next stream on success.
+ *
+ * @see rte_node_next_stream_put().
+ */
+__rte_experimental
+static inline void **
+rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+	uint16_t free_space = node->size - idx;
+
+	if (unlikely(free_space < nb_objs))
+		__rte_node_stream_alloc_size(graph, node, nb_objs);
+
+	return &node->objs[idx];
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Put the next stream to pending state in the circular buffer
+ * for further processing. Should be invoked followed by
+ * rte_node_next_stream_get().
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index..
+ * @param idx
+ *   Number of objs updated in the stream after getting the stream using
+ *   rte_node_next_stream_get.
+ *
+ * @see rte_node_next_stream_get().
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t idx)
+{
+	if (unlikely(!idx))
+		return;
+
+	node = __rte_node_next_node_get(node, next);
+	if (node->idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	node->idx += idx;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Home run scenario, Enqueue all the objs of current node to next
+ * node in optimized way by swapping the streams of both nodes.
+ * Performs good when next node is already not in pending state.
+ * If next node is already in pending state then normal enqueue
+ * will be used.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param src
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index.
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_move(struct rte_graph *graph, struct rte_node *src,
+			  rte_edge_t next)
+{
+	struct rte_node *dst = __rte_node_next_node_get(src, next);
+
+	/* Let swap the pointers if dst don't have valid objs */
+	if (likely(dst->idx == 0)) {
+		void **dobjs = dst->objs;
+		uint16_t dsz = dst->size;
+		dst->objs = src->objs;
+		dst->size = src->size;
+		src->objs = dobjs;
+		src->size = dsz;
+		dst->idx = src->idx;
+		__rte_node_enqueue_tail_update(graph, dst);
+	} else { /* Move the objects from src node to dst node */
+		rte_node_enqueue(graph, src, next, src->objs, src->idx);
+	}
+}
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 13/29] graph: add unit test case
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (11 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 12/29] graph: implement fastpath API routines jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 14/29] graph: add performance testcase jerinj
                         ` (17 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang

From: Kiran Kumar K <kirankumark@marvell.com>

Adding the unit test to test the functionality of node and graph APIs.
Testing includes registering a node, cloning a node, creating a graph,
perform graph walk, collecting stats and all node and graph debug APIs.

example command to test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 app/test/Makefile     |   6 +
 app/test/meson.build  |   6 +-
 app/test/test_graph.c | 819 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 830 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_graph.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 1f080d162..ce2e08e12 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -221,6 +221,10 @@ SRCS-y += test_event_timer_adapter.c
 SRCS-y += test_event_crypto_adapter.c
 endif
 
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
+SRCS-y += test_graph.c
+endif
+
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
 SRCS-y += test_rawdev.c
 endif
@@ -240,6 +244,8 @@ endif
 CFLAGS += -DALLOW_EXPERIMENTAL_API
 
 CFLAGS += -O3
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
 CFLAGS += $(WERROR_FLAGS)
 
 LDLIBS += -lm
diff --git a/app/test/meson.build b/app/test/meson.build
index 351d29cb6..3cf850584 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -51,6 +51,7 @@ test_sources = files('commands.c',
 	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
+	'test_graph.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
@@ -151,7 +152,8 @@ test_deps = ['acl',
 	'rib',
 	'ring',
 	'stack',
-	'timer'
+	'timer',
+	'graph'
 ]
 
 # Each test is marked with flag true/false
@@ -362,6 +364,8 @@ endif
 
 # specify -D_GNU_SOURCE unconditionally
 cflags += '-D_GNU_SOURCE'
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
 
 test_dep_objs = []
 if dpdk_conf.has('RTE_LIBRTE_COMPRESSDEV')
diff --git a/app/test/test_graph.c b/app/test/test_graph.c
new file mode 100644
index 000000000..a90dc8f30
--- /dev/null
+++ b/app/test/test_graph.c
@@ -0,0 +1,819 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static uint16_t test_node_worker_source(struct rte_graph *graph,
+					struct rte_node *node, void **objs,
+					uint16_t nb_objs);
+
+static uint16_t test_node0_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node1_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node2_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node3_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+#define MBUFF_SIZE 512
+#define MAX_NODES  4
+
+static struct rte_mbuf mbuf[MAX_NODES + 1][MBUFF_SIZE];
+static void *mbuf_p[MAX_NODES + 1][MBUFF_SIZE];
+static rte_graph_t graph_id;
+static uint64_t obj_stats[MAX_NODES + 1];
+static uint64_t fn_calls[MAX_NODES + 1];
+
+const char *node_patterns[] = {
+	"test_node_source1",	   "test_node00",
+	"test_node00-test_node11", "test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+const char *node_names[] = {
+	"test_node00",
+	"test_node00-test_node11",
+	"test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+struct test_node_register {
+	char name[RTE_NODE_NAMESIZE];
+	rte_node_process_t process;
+	uint16_t nb_edges;
+	const char *next_nodes[MAX_NODES];
+};
+
+typedef struct {
+	uint32_t idx;
+	struct test_node_register node;
+} test_node_t;
+
+typedef struct {
+	test_node_t test_node[MAX_NODES];
+} test_main_t;
+
+static test_main_t test_main = {
+	.test_node = {
+		{
+			.node = {
+					.name = "test_node00",
+					.process = test_node0_worker,
+					.nb_edges = 2,
+					.next_nodes = {"test_node00-"
+						       "test_node11",
+						       "test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node11",
+					.process = test_node1_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node22",
+					.process = test_node2_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node33"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node33",
+					.process = test_node3_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00"},
+				},
+		},
+	},
+};
+
+static int
+node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	*(uint32_t *)node->ctx = node->id;
+
+	return 0;
+}
+
+static struct rte_node_register test_node_source = {
+	.name = "test_node_source1",
+	.process = test_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.nb_edges = 2,
+	.init = node_init,
+	.next_nodes = {"test_node00", "test_node00-test_node11"},
+};
+RTE_NODE_REGISTER(test_node_source);
+
+static struct rte_node_register test_node0 = {
+	.name = "test_node00",
+	.process = test_node0_worker,
+	.init = node_init,
+};
+RTE_NODE_REGISTER(test_node0);
+
+uint16_t
+test_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	uint32_t obj_node0 = rand() % 100, obj_node1;
+	test_main_t *tm = &test_main;
+	struct rte_mbuf *data;
+	void **next_stream;
+	rte_node_t next;
+	uint32_t i;
+
+	RTE_SET_USED(objs);
+	nb_objs = RTE_GRAPH_BURST_SIZE;
+
+	/* Prepare stream for next node 0 */
+	obj_node0 = nb_objs * obj_node0 * 0.01;
+	next = 0;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node0);
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[0][i];
+		data->udata64 = ((uint64_t)tm->test_node[0].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][i];
+	}
+	rte_node_next_stream_put(graph, node, next, obj_node0);
+
+	/* Prepare stream for next node 1 */
+	obj_node1 = nb_objs - obj_node0;
+	next = 1;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node1);
+	for (i = 0; i < obj_node1; i++) {
+		data = &mbuf[0][obj_node0 + i];
+		data->udata64 = ((uint64_t)tm->test_node[1].idx << 32) | i;
+		if ((i + 1) == obj_node1)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][obj_node0 + i];
+	}
+
+	rte_node_next_stream_put(graph, node, next, obj_node1);
+	obj_stats[0] += nb_objs;
+	fn_calls[0] += 1;
+	return nb_objs;
+}
+
+uint16_t
+test_node0_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+
+	if (*(uint32_t *)node->ctx == test_node0.id) {
+		uint32_t obj_node0 = rand() % 100, obj_node1;
+		struct rte_mbuf *data;
+		uint8_t second_pass = 0;
+		uint32_t count = 0;
+		uint32_t i;
+
+		obj_stats[1] += nb_objs;
+		fn_calls[1] += 1;
+
+		for (i = 0; i < nb_objs; i++) {
+			data = (struct rte_mbuf *)objs[i];
+			if ((data->udata64 >> 32) != tm->test_node[0].idx) {
+				printf("Data idx miss match at node 0, expected"
+				       " = %u got = %u\n",
+				       tm->test_node[0].idx,
+				       (uint32_t)(data->udata64 >> 32));
+				goto end;
+			}
+
+			if ((data->udata64 & 0xffff) != (i - count)) {
+				printf("Expected buff count miss match at "
+				       "node 0\n");
+				goto end;
+			}
+
+			if (data->udata64 & (0x1 << 16))
+				count = i + 1;
+			if (data->udata64 & (0x1 << 17))
+				second_pass = 1;
+		}
+
+		if (count != i) {
+			printf("Count mismatch at node 0\n");
+			goto end;
+		}
+
+		obj_node0 = nb_objs * obj_node0 * 0.01;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[1][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[1].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[1][0],
+				 obj_node0);
+
+		obj_node1 = nb_objs - obj_node0;
+		for (i = 0; i < obj_node1; i++) {
+			data = &mbuf[1][obj_node0 + i];
+			data->udata64 =
+				((uint64_t)tm->test_node[2].idx << 32) | i;
+			if ((i + 1) == obj_node1)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 1, (void **)&mbuf_p[1][obj_node0],
+				 obj_node1);
+
+	} else if (*(uint32_t *)node->ctx == tm->test_node[1].idx) {
+		test_node1_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[2].idx) {
+		test_node2_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[3].idx) {
+		test_node3_worker(graph, node, objs, nb_objs);
+	} else {
+		printf("Unexpected node context\n");
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node1_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	uint32_t obj_node0 = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t i;
+
+	obj_stats[2] += nb_objs;
+	fn_calls[2] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[1].idx) {
+			printf("Data idx miss match at node 1, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[1].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 1\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 1\n");
+		goto end;
+	}
+
+	obj_node0 = nb_objs;
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[2][i];
+		data->udata64 = ((uint64_t)tm->test_node[2].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		if (second_pass)
+			data->udata64 |= (1 << 17);
+	}
+	rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[2][0], obj_node0);
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node2_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[3] += nb_objs;
+	fn_calls[3] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[2].idx) {
+			printf("Data idx miss match at node 2, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[2].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 2\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 2\n");
+		goto end;
+	}
+
+	if (!second_pass) {
+		obj_node0 = nb_objs;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[3][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[3].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[3][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node3_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[4] += nb_objs;
+	fn_calls[4] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[3].idx) {
+			printf("Data idx miss match at node 3, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[3].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 3\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 3\n");
+		goto end;
+	}
+
+	if (second_pass) {
+		printf("Unexpected buffers are at node 3\n");
+		goto end;
+	} else {
+		obj_node0 = nb_objs * 2;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[4][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[0].idx << 32) | i;
+			data->udata64 |= (1 << 17);
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[4][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+static int
+test_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	int i;
+
+	/* Verify the name with ID */
+	for (i = 1; i < MAX_NODES; i++) {
+		char *name = rte_node_id_to_name(tm->test_node[i].idx);
+		if (strcmp(name, node_names[i]) != 0) {
+			printf("Test node name verify by ID = %d failed "
+			       "Expected = %s, got %s\n",
+			       i, node_names[i], name);
+			return -1;
+		}
+	}
+
+	/* Verify by name */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t idx = rte_node_from_name(node_names[i]);
+		if (idx != tm->test_node[i].idx) {
+			printf("Test node ID verify by name = %s failed "
+			       "Expected = %d, got %d\n",
+			       node_names[i], tm->test_node[i].idx, idx);
+			return -1;
+		}
+	}
+
+	/* Verify edge count */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t count = rte_node_edge_count(tm->test_node[i].idx);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+	}
+
+	/* Verify edge names */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t j, count;
+		char **next_edges;
+
+		count = rte_node_edge_get(tm->test_node[i].idx, NULL);
+		if (count != tm->test_node[i].node.nb_edges * sizeof(char *)) {
+			printf("Test number of edge count for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+		next_edges = malloc(count);
+		count = rte_node_edge_get(tm->test_node[i].idx, next_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		for (j = 0; j < count; j++) {
+			if (strcmp(next_edges[j],
+				   tm->test_node[i].node.next_nodes[j]) != 0) {
+				printf("Edge name miss match, expected = %s got = %s\n",
+				       tm->test_node[i].node.next_nodes[j],
+				       next_edges[j]);
+				return -1;
+			}
+		}
+		free(next_edges);
+	}
+
+	return 0;
+}
+
+static int
+test_node_clone(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id, dummy_id;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	tm->test_node[0].idx = node_id;
+
+	/* Clone with same name, should fail */
+	dummy_id = rte_node_clone(node_id, "test_node00");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid id when clone with same name, Expecting fail\n");
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		tm->test_node[i].idx =
+			rte_node_clone(node_id, tm->test_node[i].node.name);
+		if (rte_node_is_invalid(tm->test_node[i].idx)) {
+			printf("Got invalid node id\n");
+			return -1;
+		}
+	}
+
+	/* Clone from cloned node should fail */
+	dummy_id = rte_node_clone(tm->test_node[1].idx, "dummy_node");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid node id when cloning from cloned node, expected fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_update_edges(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id;
+	uint16_t count;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	count = rte_node_edge_update(node_id, 0,
+				     tm->test_node[0].node.next_nodes,
+				     tm->test_node[0].node.nb_edges);
+	if (count != tm->test_node[0].node.nb_edges) {
+		printf("Update edges failed expected: %d got = %d\n",
+		       tm->test_node[0].node.nb_edges, count);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		count = rte_node_edge_update(tm->test_node[i].idx, 0,
+					     tm->test_node[i].node.next_nodes,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Update edges failed expected: %d got = %d\n",
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		count = rte_node_edge_shrink(tm->test_node[i].idx,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Shrink edges failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_create_graph(void)
+{
+	static const char *node_patterns_dummy[] = {
+		"test_node_source1",	   "test_node00",
+		"test_node00-test_node11", "test_node00-test_node22",
+		"test_node00-test_node33", "test_node00-dummy_node",
+	};
+	struct rte_graph_param gconf = {
+		.socket_id = SOCKET_ID_ANY,
+		.nb_node_patterns = 6,
+		.node_patterns = node_patterns_dummy,
+	};
+	uint32_t dummy_node_id;
+	uint32_t node_id;
+
+	node_id = rte_node_from_name("test_node00");
+	dummy_node_id = rte_node_clone(node_id, "dummy_node");
+	if (rte_node_is_invalid(dummy_node_id)) {
+		printf("Got invalid node id\n");
+		return -1;
+	}
+
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id != RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation success with isolated node, expected graph creation fail\n");
+		return -1;
+	}
+
+	gconf.nb_node_patterns = 5;
+	gconf.node_patterns = node_patterns;
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+test_graph_walk(void)
+{
+	struct rte_graph *graph = rte_graph_lookup("worker0");
+	int i;
+
+	if (!graph) {
+		printf("Graph lookup failed\n");
+		return -1;
+	}
+
+	for (i = 0; i < 5; i++)
+		rte_graph_walk(graph);
+	return 0;
+}
+
+static int
+test_graph_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	struct rte_node *node;
+	int i;
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get(graph_id, tm->test_node[i].idx);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get_by_name("worker0", node_names[i]);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+graph_cluster_stats_cb_t(bool is_first, bool is_last, void *cookie,
+			 const struct rte_graph_cluster_node_stats *st)
+{
+	int i;
+
+	RTE_SET_USED(is_first);
+	RTE_SET_USED(is_last);
+	RTE_SET_USED(cookie);
+
+	for (i = 0; i < MAX_NODES + 1; i++) {
+		rte_node_t id = rte_node_from_name(node_patterns[i]);
+		if (id == st->id) {
+			if (obj_stats[i] != st->objs) {
+				printf("Obj count miss match for node = %s expected = %"PRId64", got=%"PRId64"\n",
+				       node_patterns[i], obj_stats[i],
+				       st->objs);
+				return -1;
+			}
+
+			if (fn_calls[i] != st->calls) {
+				printf("Func call miss match for node = %s expected = %"PRId64", got = %"PRId64"\n",
+				       node_patterns[i], fn_calls[i],
+				       st->calls);
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+test_print_stats(void)
+{
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker0";
+
+	if (!rte_graph_has_stats_feature())
+		return 0;
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+	s_param.fn = graph_cluster_stats_cb_t;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL) {
+		printf("Unable to get stats\n");
+		return -1;
+	}
+	/* Clear screen and move to top left */
+	rte_graph_cluster_stats_get(stats, 0);
+	rte_graph_cluster_stats_destroy(stats);
+
+	return 0;
+}
+
+static int
+graph_setup(void)
+{
+	int i, j;
+
+	for (i = 0; i <= MAX_NODES; i++) {
+		for (j = 0; j < MBUFF_SIZE; j++)
+			mbuf_p[i][j] = &mbuf[i][j];
+	}
+	if (test_node_clone()) {
+		printf("test_node_clone: fail\n");
+		return -1;
+	}
+	printf("test_node_clone: pass\n");
+
+	return 0;
+}
+
+static void
+graph_teardown(void)
+{
+	rte_graph_t id;
+
+	id = rte_graph_destroy("worker0");
+	if (id == RTE_GRAPH_ID_INVALID)
+		printf("Graph Destroy failed\n");
+}
+
+static struct unit_test_suite graph_testsuite = {
+	.suite_name = "Graph library test suite",
+	.setup = graph_setup,
+	.teardown = graph_teardown,
+	.unit_test_cases = {
+		TEST_CASE(test_update_edges),
+		TEST_CASE(test_lookup_functions),
+		TEST_CASE(test_create_graph),
+		TEST_CASE(test_graph_lookup_functions),
+		TEST_CASE(test_graph_walk),
+		TEST_CASE(test_print_stats),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+graph_autotest_fn(void)
+{
+	return unit_test_suite_runner(&graph_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_autotest, graph_autotest_fn);
+
+static int
+test_node_list_dump(void)
+{
+	rte_node_list_dump(stdout);
+
+	return TEST_SUCCESS;
+}
+REGISTER_TEST_COMMAND(node_list_dump, test_node_list_dump);
+
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 14/29] graph: add performance testcase
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (12 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 13/29] graph: add unit test case jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 15/29] node: add log infra and null node jerinj
                         ` (16 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add unit test framework to create and test performance of various graph
models.

example command to test:

echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 app/test/Makefile          |    1 +
 app/test/meson.build       |    1 +
 app/test/test_graph_perf.c | 1057 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1059 insertions(+)
 create mode 100644 app/test/test_graph_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index ce2e08e12..77276f300 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -223,6 +223,7 @@ endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
 SRCS-y += test_graph.c
+SRCS-y += test_graph_perf.c
 endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
diff --git a/app/test/meson.build b/app/test/meson.build
index 3cf850584..9006cc074 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -52,6 +52,7 @@ test_sources = files('commands.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_graph.c',
+	'test_graph_perf.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
diff --git a/app/test/test_graph_perf.c b/app/test/test_graph_perf.c
new file mode 100644
index 000000000..a629f1e35
--- /dev/null
+++ b/app/test/test_graph_perf.c
@@ -0,0 +1,1057 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_lcore.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+#define TEST_GRAPH_PERF_MZ	     "graph_perf_data"
+#define TEST_GRAPH_SRC_NAME	     "test_graph_perf_source"
+#define TEST_GRAPH_SRC_BRST_ONE_NAME "test_graph_perf_source_one"
+#define TEST_GRAPH_WRK_NAME	     "test_graph_perf_worker"
+#define TEST_GRAPH_SNK_NAME	     "test_graph_perf_sink"
+
+#define SOURCES(map)	     RTE_DIM(map)
+#define STAGES(map)	     RTE_DIM(map)
+#define NODES_PER_STAGE(map) RTE_DIM(map[0])
+#define SINKS(map)	     RTE_DIM(map[0])
+
+#define MAX_EDGES_PER_NODE 7
+
+struct test_node_data {
+	uint8_t node_id;
+	uint8_t is_sink;
+	uint8_t next_nodes[MAX_EDGES_PER_NODE];
+	uint8_t next_percentage[MAX_EDGES_PER_NODE];
+};
+
+struct test_graph_perf {
+	uint16_t nb_nodes;
+	rte_graph_t graph_id;
+	struct test_node_data *node_data;
+};
+
+struct graph_lcore_data {
+	uint8_t done;
+	rte_graph_t graph_id;
+};
+
+static struct test_node_data *
+graph_get_node_data(struct test_graph_perf *graph_data, rte_node_t id)
+{
+	struct test_node_data *node_data = NULL;
+	int i;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		if (graph_data->node_data[i].node_id == id) {
+			node_data = &graph_data->node_data[i];
+			break;
+		}
+
+	return node_data;
+}
+
+static int
+test_node_ctx_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	struct test_graph_perf *graph_data;
+	struct test_node_data *node_data;
+	const struct rte_memzone *mz;
+	rte_node_t nid = node->id;
+	rte_edge_t edge = 0;
+	int i;
+
+	RTE_SET_USED(graph);
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+	node_data = graph_get_node_data(graph_data, nid);
+	node->ctx[0] = node->nb_edges;
+	for (i = 0; i < node->nb_edges && !node_data->is_sink; i++, edge++) {
+		node->ctx[i + 1] = edge;
+		node->ctx[i + 9] = node_data->next_percentage[i];
+	}
+
+	return 0;
+}
+
+/* Source node function */
+static uint16_t
+test_perf_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			     void **objs, uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9] * RTE_GRAPH_BURST_SIZE) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return RTE_GRAPH_BURST_SIZE;
+}
+
+static struct rte_node_register test_graph_perf_source = {
+	.name = TEST_GRAPH_SRC_NAME,
+	.process = test_perf_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source);
+
+static uint16_t
+test_perf_node_worker_source_burst_one(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9]) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return 1;
+}
+
+static struct rte_node_register test_graph_perf_source_burst_one = {
+	.name = TEST_GRAPH_SRC_BRST_ONE_NAME,
+	.process = test_perf_node_worker_source_burst_one,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source_burst_one);
+
+/* Worker node function */
+static uint16_t
+test_perf_node_worker(struct rte_graph *graph, struct rte_node *node,
+		      void **objs, uint16_t nb_objs)
+{
+	uint16_t next = 0;
+	uint16_t enq = 0;
+	uint16_t count;
+	int i;
+
+	/* Move stream for single next node */
+	if (node->ctx[0] == 1) {
+		rte_node_next_stream_move(graph, node, node->ctx[1]);
+		return nb_objs;
+	}
+
+	/* Enqueue objects to next nodes proportionally */
+	for (i = 0; i < node->ctx[0]; i++) {
+		next = node->ctx[i + 1];
+		count = (node->ctx[i + 9] * nb_objs) / 100;
+		enq += count;
+		while (count) {
+			switch (count & (4 - 1)) {
+			case 0:
+				rte_node_enqueue_x4(graph, node, next, objs[0],
+						    objs[1], objs[2], objs[3]);
+				objs += 4;
+				count -= 4;
+				break;
+			case 1:
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 1;
+				count -= 1;
+				break;
+			case 2:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				objs += 2;
+				count -= 2;
+				break;
+			case 3:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 3;
+				count -= 3;
+				break;
+			}
+		}
+	}
+
+	if (enq != nb_objs)
+		rte_node_enqueue(graph, node, next, objs, nb_objs - enq);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_worker = {
+	.name = TEST_GRAPH_WRK_NAME,
+	.process = test_perf_node_worker,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_worker);
+
+/* Last node in graph a.k.a sink node */
+static uint16_t
+test_perf_node_sink(struct rte_graph *graph, struct rte_node *node, void **objs,
+		    uint16_t nb_objs)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_sink = {
+	.name = TEST_GRAPH_SNK_NAME,
+	.process = test_perf_node_sink,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_sink);
+
+static int
+graph_perf_setup(void)
+{
+	if (rte_lcore_count() < 2) {
+		printf("Test requires at least 2 lcores\n");
+		return TEST_SKIPPED;
+	}
+
+	return 0;
+}
+
+static void
+graph_perf_teardown(void)
+{
+}
+
+static inline rte_node_t
+graph_node_get(const char *pname, char *nname)
+{
+	rte_node_t pnode_id = rte_node_from_name(pname);
+	char lookup_name[RTE_NODE_NAMESIZE];
+	rte_node_t node_id;
+
+	snprintf(lookup_name, RTE_NODE_NAMESIZE, "%s-%s", pname, nname);
+	node_id = rte_node_from_name(lookup_name);
+
+	if (node_id != RTE_NODE_ID_INVALID) {
+		if (rte_node_edge_count(node_id))
+			rte_node_edge_shrink(node_id, 0);
+		return node_id;
+	}
+
+	return rte_node_clone(pnode_id, nname);
+}
+
+static uint16_t
+graph_node_count_edges(uint32_t stage, uint16_t node, uint16_t nodes_per_stage,
+		       uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+		       char *ename[], struct test_node_data *node_data,
+		       rte_node_t **node_map)
+{
+	uint8_t total_percent = 0;
+	uint16_t edges = 0;
+	int i;
+
+	for (i = 0; i < nodes_per_stage && edges < MAX_EDGES_PER_NODE; i++) {
+		if (edge_map[stage + 1][i][node]) {
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[stage + 1][i]));
+			node_data->next_nodes[edges] = node_map[stage + 1][i];
+			node_data->next_percentage[edges] =
+				edge_map[stage + 1][i][node];
+			edges++;
+			total_percent += edge_map[stage + 1][i][node];
+		}
+	}
+
+	if (edges >= MAX_EDGES_PER_NODE || (edges && total_percent != 100)) {
+		for (i = 0; i < edges; i++)
+			free(ename[i]);
+		return RTE_EDGE_ID_INVALID;
+	}
+
+	return edges;
+}
+
+static int
+graph_init(const char *gname, uint8_t nb_srcs, uint8_t nb_sinks,
+	   uint32_t stages, uint16_t nodes_per_stage,
+	   uint8_t src_map[][nodes_per_stage], uint8_t snk_map[][nb_sinks],
+	   uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+	   uint8_t burst_one)
+{
+	struct test_graph_perf *graph_data;
+	char nname[RTE_NODE_NAMESIZE / 2];
+	struct test_node_data *node_data;
+	char *ename[nodes_per_stage];
+	struct rte_graph_param gconf;
+	const struct rte_memzone *mz;
+	uint8_t total_percent = 0;
+	rte_node_t *src_nodes;
+	rte_node_t *snk_nodes;
+	rte_node_t **node_map;
+	char **node_patterns;
+	rte_graph_t graph_id;
+	rte_edge_t edges;
+	rte_edge_t count;
+	uint32_t i, j, k;
+
+	mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
+				 sizeof(struct test_graph_perf), 0, 0);
+	if (mz == NULL) {
+		printf("Failed to allocate graph common memory\n");
+		return -ENOMEM;
+	}
+
+	graph_data = mz->addr;
+	graph_data->nb_nodes = 0;
+	graph_data->node_data =
+		malloc(sizeof(struct test_node_data) *
+		       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (graph_data->node_data == NULL) {
+		printf("Failed to reserve memzone for graph data\n");
+		goto memzone_free;
+	}
+
+	node_patterns = malloc(sizeof(char *) *
+			       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (node_patterns == NULL) {
+		printf("Failed to reserve memory for node patterns\n");
+		goto data_free;
+	}
+
+	src_nodes = malloc(sizeof(rte_node_t) * nb_srcs);
+	if (src_nodes == NULL) {
+		printf("Failed to reserve memory for src nodes\n");
+		goto pattern_free;
+	}
+
+	snk_nodes = malloc(sizeof(rte_node_t) * nb_sinks);
+	if (snk_nodes == NULL) {
+		printf("Failed to reserve memory for snk nodes\n");
+		goto src_free;
+	}
+
+	node_map = malloc(sizeof(rte_node_t *) * stages +
+			  sizeof(rte_node_t) * nodes_per_stage * stages);
+	if (node_map == NULL) {
+		printf("Failed to reserve memory for node map\n");
+		goto snk_free;
+	}
+
+	/* Setup the Graph */
+	for (i = 0; i < stages; i++) {
+		node_map[i] =
+			(rte_node_t *)(node_map + stages) + nodes_per_stage * i;
+		for (j = 0; j < nodes_per_stage; j++) {
+			total_percent = 0;
+			for (k = 0; k < nodes_per_stage; k++)
+				total_percent += edge_map[i][j][k];
+			if (!total_percent)
+				continue;
+			node_patterns[graph_data->nb_nodes] =
+				malloc(RTE_NODE_NAMESIZE);
+			if (node_patterns[graph_data->nb_nodes] == NULL) {
+				printf("Failed to create memory for pattern\n");
+				goto pattern_name_free;
+			}
+
+			/* Clone a worker node */
+			snprintf(nname, sizeof(nname), "%d-%d", i, j);
+			node_map[i][j] =
+				graph_node_get(TEST_GRAPH_WRK_NAME, nname);
+			if (node_map[i][j] == RTE_NODE_ID_INVALID) {
+				printf("Failed to create node[%s]\n", nname);
+				graph_data->nb_nodes++;
+				goto pattern_name_free;
+			}
+			snprintf(node_patterns[graph_data->nb_nodes],
+				 RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[i][j]));
+			node_data =
+				&graph_data->node_data[graph_data->nb_nodes];
+			node_data->node_id = node_map[i][j];
+			node_data->is_sink = false;
+			graph_data->nb_nodes++;
+		}
+	}
+
+	for (i = 0; i < stages - 1; i++) {
+		for (j = 0; j < nodes_per_stage; j++) {
+			/* Count edges i.e connections of worker node to next */
+			node_data =
+				graph_get_node_data(graph_data, node_map[i][j]);
+			edges = graph_node_count_edges(i, j, nodes_per_stage,
+						       edge_map, ename,
+						       node_data, node_map);
+			if (edges == RTE_EDGE_ID_INVALID) {
+				printf("Invalid edge configuration\n");
+				goto pattern_name_free;
+			}
+			if (!edges)
+				continue;
+
+			/* Connect a node in stage 'i' to nodes
+			 * in stage 'i + 1' with edges.
+			 */
+			count = rte_node_edge_update(
+				node_map[i][j], 0,
+				(const char **)(uintptr_t)ename, edges);
+			for (k = 0; k < edges; k++)
+				free(ename[k]);
+			if (count != edges) {
+				printf("Couldn't add edges %d %d\n", edges,
+				       count);
+				goto pattern_name_free;
+			}
+		}
+	}
+
+	/* Setup Source nodes */
+	for (i = 0; i < nb_srcs; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+		/* Clone a source node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		src_nodes[i] =
+			graph_node_get(burst_one ? TEST_GRAPH_SRC_BRST_ONE_NAME
+						 : TEST_GRAPH_SRC_NAME,
+				       nname);
+		if (src_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(src_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = src_nodes[i];
+		node_data->is_sink = false;
+		graph_data->nb_nodes++;
+
+		/* Prepare next node list  to connect to */
+		for (j = 0; j < nodes_per_stage; j++) {
+			if (!src_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[0][j]));
+			node_data->next_nodes[edges] = node_map[0][j];
+			node_data->next_percentage[edges] = src_map[i][j];
+			edges++;
+			total_percent += src_map[i][j];
+		}
+
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[j]);
+			goto pattern_name_free;
+		}
+
+		/* Connect to list of next nodes using edges */
+		count = rte_node_edge_update(src_nodes[i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Setup Sink nodes */
+	for (i = 0; i < nb_sinks; i++) {
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+
+		/* Clone a sink node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		snk_nodes[i] = graph_node_get(TEST_GRAPH_SNK_NAME, nname);
+		if (snk_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(snk_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = snk_nodes[i];
+		node_data->is_sink = true;
+		graph_data->nb_nodes++;
+	}
+
+	/* Connect last stage worker nodes to sink nodes */
+	for (i = 0; i < nodes_per_stage; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_data = graph_get_node_data(graph_data,
+						node_map[stages - 1][i]);
+		/* Prepare list of sink nodes to connect to */
+		for (j = 0; j < nb_sinks; j++) {
+			if (!snk_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(snk_nodes[j]));
+			node_data->next_nodes[edges] = snk_nodes[j];
+			node_data->next_percentage[edges] = snk_map[i][j];
+			edges++;
+			total_percent += snk_map[i][j];
+		}
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[i]);
+			goto pattern_name_free;
+		}
+
+		/* Connect a worker node to a list of sink nodes */
+		count = rte_node_edge_update(node_map[stages - 1][i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Create a Graph */
+	gconf.socket_id = SOCKET_ID_ANY;
+	gconf.nb_node_patterns = graph_data->nb_nodes;
+	gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
+
+	graph_id = rte_graph_create(gname, &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		goto pattern_name_free;
+	}
+	graph_data->graph_id = graph_id;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+	free(snk_nodes);
+	free(src_nodes);
+	free(node_patterns);
+	return 0;
+
+pattern_name_free:
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+snk_free:
+	free(snk_nodes);
+src_free:
+	free(src_nodes);
+pattern_free:
+	free(node_patterns);
+data_free:
+	free(graph_data->node_data);
+memzone_free:
+	rte_memzone_free(mz);
+	return -ENOMEM;
+}
+
+/* Worker thread function */
+static int
+_graph_perf_wrapper(void *args)
+{
+	struct graph_lcore_data *data = args;
+	struct rte_graph *graph;
+
+	/* Lookup graph */
+	graph = rte_graph_lookup(rte_graph_id_to_name(data->graph_id));
+
+	/* Graph walk until done */
+	while (!data->done)
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
+static int
+measure_perf_get(rte_graph_t graph_id)
+{
+	const char *pattern = rte_graph_id_to_name(graph_id);
+	uint32_t lcore_id = rte_get_next_lcore(-1, 1, 0);
+	struct rte_graph_cluster_stats_param param;
+	struct rte_graph_cluster_stats *stats;
+	struct graph_lcore_data *data;
+
+	data = rte_zmalloc("Graph_perf", sizeof(struct graph_lcore_data),
+			   RTE_CACHE_LINE_SIZE);
+	data->graph_id = graph_id;
+	data->done = 0;
+
+	/* Run graph worker thread function */
+	rte_eal_remote_launch(_graph_perf_wrapper, data, lcore_id);
+
+	/* Collect stats for few msecs */
+	if (rte_graph_has_stats_feature()) {
+		memset(&param, 0, sizeof(param));
+		param.f = stdout;
+		param.socket_id = SOCKET_ID_ANY;
+		param.graph_patterns = &pattern;
+		param.nb_graph_patterns = 1;
+
+		stats = rte_graph_cluster_stats_create(&param);
+		if (stats == NULL) {
+			printf("Failed to create stats\n");
+			return -ENOMEM;
+		}
+
+		rte_delay_ms(3E2);
+		rte_graph_cluster_stats_get(stats, true);
+		rte_delay_ms(1E3);
+		rte_graph_cluster_stats_get(stats, false);
+		rte_graph_cluster_stats_destroy(stats);
+	} else
+		rte_delay_ms(1E3);
+
+	data->done = 1;
+	rte_eal_wait_lcore(lcore_id);
+
+	return 0;
+}
+
+static inline void
+graph_fini(void)
+{
+	const struct rte_memzone *mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	struct test_graph_perf *graph_data;
+
+	if (mz == NULL)
+		return;
+	graph_data = mz->addr;
+
+	rte_graph_destroy(rte_graph_id_to_name(graph_data->graph_id));
+	free(graph_data->node_data);
+	rte_memzone_free(rte_memzone_lookup(TEST_GRAPH_PERF_MZ));
+}
+
+static int
+measure_perf(void)
+{
+	const struct rte_memzone *mz;
+	struct test_graph_perf *graph_data;
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+
+	return measure_perf_get(graph_data->graph_id);
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk_brst_one(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_2src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_2snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_tree_4s_4n_1src_4snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_reverse_tree_3s_4n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_parallel_tree_5s_4n_4src_4snk(void)
+{
+	return measure_perf();
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr_brst_one(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 1);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			2
+ * sink:		1
+ */
+static inline int
+graph_init_hr_multi_src(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = {
+		{100}, {100}
+	};
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		2
+ */
+static inline int
+graph_init_hr_multi_snk(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][2] = { {50, 50} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		4
+ * src:			1
+ * sink:		4
+ */
+static inline int
+graph_init_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 0, 0, 0},
+			{50, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{33, 33, 0, 0},
+			{34, 34, 0, 0},
+			{33, 33, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0}
+		}
+	};
+	uint8_t src_map[][4] = { {100, 0, 0, 0} };
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		3
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_reverse_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25}
+		},
+		{
+			{33, 33, 33, 33},
+			{33, 33, 33, 33},
+			{34, 34, 34, 34},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 50, 50, 0},
+			{50, 50, 50, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+	};
+	uint8_t src_map[][4] = { {25, 25, 25, 25} };
+	uint8_t snk_map[][1] = { {100}, {100}, {0}, {0} };
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		5
+ * src:			4
+ * sink:		4
+ */
+static inline int
+graph_init_parallel_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+	};
+	uint8_t src_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_parallel", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/** Graph Creation cheat sheet
+ *  edge_map -> dictates graph flow from worker stage 0 to worker stage n-1.
+ *  src_map  -> dictates source nodes enqueue percentage to worker stage 0.
+ *  snk_map  -> dictates stage n-1 enqueue percentage to sink.
+ *
+ *  Layout:
+ *  edge_map[<nb_stages>][<nodes_per_stg>][<nodes_in_nxt_stg = nodes_per_stg>]
+ *  src_map[<nb_sources>][<nodes_in_stage0 = nodes_per_stage>]
+ *  snk_map[<nodes_in_stage(n-1) = nodes_per_stage>][<nb_sinks>]
+ *
+ *  The last array dictates the percentage of received objs to enqueue to next
+ *  stage.
+ *
+ *  Note: edge_map[][0][] will always be unused as it will receive from source
+ *
+ *  Example:
+ *	Graph:
+ *	http://bit.ly/2PqbqOy
+ *	Each stage(n) connects to all nodes in the next stage in decreasing
+ *	order.
+ *	Since we can't resize the edge_map dynamically we get away by creating
+ *	dummy nodes and assigning 0 percentages.
+ *	Max nodes across all stages = 4
+ *	stages = 3
+ *	nb_src = 1
+ *	nb_snk = 1
+ *			   // Stages
+ *	edge_map[][4][4] = {
+ *		// Nodes per stage
+ *		{
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25}
+ *		},	// This will be unused.
+ *		{
+ *		    // Nodes enabled in current stage + prev stage enq %
+ *		    {33, 33, 33, 33},
+ *		    {33, 33, 33, 33},
+ *		    {34, 34, 34, 34},
+ *		    {0, 0, 0, 0}
+ *		},
+ *		{
+ *		    {50, 50, 50, 0},
+ *		    {50, 50, 50, 0},
+ *		    {0, 0, 0, 0},
+ *		    {0, 0, 0, 0}
+ *		},
+ *	};
+ *	Above, each stage tells how much it should receive from previous except
+ *	from stage_0.
+ *
+ *	src_map[][4] = { {25, 25, 25, 25} };
+ *	Here, we tell each source the % it has to send to stage_0 nodes. In
+ *	case we want 2 source node we can declare as
+ *	src_map[][4] = { {25, 25, 25, 25}, {25, 25, 25, 25} };
+ *
+ *	snk_map[][1] = { {100}, {100}, {0}, {0} }
+ *	Here, we tell stage - 1 nodes how much to enqueue to sink_0.
+ *	If we have 2 sinks we can do as follows
+ *	snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
+ */
+
+static struct unit_test_suite graph_perf_testsuite = {
+	.suite_name = "Graph library performance test suite",
+	.setup = graph_perf_setup,
+	.teardown = graph_perf_teardown,
+	.unit_test_cases = {
+		TEST_CASE_ST(graph_init_hr, graph_fini,
+			     graph_hr_4s_1n_1src_1snk),
+		TEST_CASE_ST(graph_init_hr_brst_one, graph_fini,
+			     graph_hr_4s_1n_1src_1snk_brst_one),
+		TEST_CASE_ST(graph_init_hr_multi_src, graph_fini,
+			     graph_hr_4s_1n_2src_1snk),
+		TEST_CASE_ST(graph_init_hr_multi_snk, graph_fini,
+			     graph_hr_4s_1n_1src_2snk),
+		TEST_CASE_ST(graph_init_tree, graph_fini,
+			     graph_tree_4s_4n_1src_4snk),
+		TEST_CASE_ST(graph_init_reverse_tree, graph_fini,
+			     graph_reverse_tree_3s_4n_1src_1snk),
+		TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
+			     graph_parallel_tree_5s_4n_4src_4snk),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+test_graph_perf_func(void)
+{
+	return unit_test_suite_runner(&graph_perf_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_perf_autotest, test_graph_perf_func);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 15/29] node: add log infra and null node
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (13 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 14/29] graph: add performance testcase jerinj
@ 2020-04-05  8:55       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 16/29] node: add ethdev Rx node jerinj
                         ` (15 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic,
	Nithin Dabilpuram, Pavan Nikhilesh, Bruce Richardson
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark, xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add log infra for node specific logging.
Also, add null rte_node that just ignores all the objects
directed to it.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 MAINTAINERS                          |  5 +++++
 app/test/meson.build                 |  7 +++++--
 config/common_base                   |  5 +++++
 doc/api/doxy-api.conf.in             |  1 +
 lib/Makefile                         |  3 +++
 lib/librte_node/Makefile             | 22 ++++++++++++++++++++++
 lib/librte_node/log.c                | 14 ++++++++++++++
 lib/librte_node/meson.build          |  8 ++++++++
 lib/librte_node/node_private.h       | 22 ++++++++++++++++++++++
 lib/librte_node/null.c               | 23 +++++++++++++++++++++++
 lib/librte_node/rte_node_version.map |  6 ++++++
 lib/meson.build                      |  5 ++++-
 meson.build                          |  1 +
 mk/rte.app.mk                        |  1 +
 14 files changed, 120 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/rte_node_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index aa40cc92b..55fb9bbb0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1467,6 +1467,11 @@ M: Jerin Jacob <jerinj@marvell.com>
 M: Kiran Kumar K <kirankumark@marvell.com>
 F: lib/librte_graph/
 
+Nodes - EXPERIMENTAL
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+M: Pavan Nikhilesh <pbhagavatula@marvell.com>
+F: lib/librte_node/
+
 
 Test Applications
 -----------------
diff --git a/app/test/meson.build b/app/test/meson.build
index 9006cc074..728d20c1f 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -154,7 +154,8 @@ test_deps = ['acl',
 	'ring',
 	'stack',
 	'timer',
-	'graph'
+	'graph',
+	'node'
 ]
 
 # Each test is marked with flag true/false
@@ -390,13 +391,15 @@ endforeach
 test_dep_objs += cc.find_library('execinfo', required: false)
 
 link_libs = []
+link_nodes = []
 if get_option('default_library') == 'static'
 	link_libs = dpdk_drivers
+	link_nodes = dpdk_graph_nodes
 endif
 
 dpdk_test = executable('dpdk-test',
 	test_sources,
-	link_whole: link_libs,
+	link_whole: link_libs + link_nodes,
 	dependencies: test_dep_objs,
 	c_args: [cflags, '-DALLOW_EXPERIMENTAL_API'],
 	install_rpath: driver_install_path,
diff --git a/config/common_base b/config/common_base
index 32f982136..1ed5187dc 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1081,6 +1081,11 @@ CONFIG_RTE_LIBRTE_GRAPH=y
 CONFIG_RTE_GRAPH_BURST_SIZE=256
 CONFIG_RTE_LIBRTE_GRAPH_STATS=y
 
+#
+# Compile librte_node
+#
+CONFIG_RTE_LIBRTE_NODE=y
+
 #
 # Compile the test application
 #
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index e3b7f54f8..92122125a 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -49,6 +49,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_mempool \
                           @TOPDIR@/lib/librte_meter \
                           @TOPDIR@/lib/librte_metrics \
+                          @TOPDIR@/lib/librte_node \
                           @TOPDIR@/lib/librte_net \
                           @TOPDIR@/lib/librte_pci \
                           @TOPDIR@/lib/librte_pdump \
diff --git a/lib/Makefile b/lib/Makefile
index 1f572b659..50d61a338 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -122,6 +122,9 @@ DEPDIRS-librte_rcu := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
 DEPDIRS-librte_graph := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_NODE) += librte_node
+DEPDIRS-librte_node := librte_graph librte_lpm librte_ethdev librte_mbuf
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
new file mode 100644
index 000000000..dbc8e1d44
--- /dev/null
+++ b/lib/librte_node/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_node.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
+LDLIBS += -lrte_eal -lrte_graph
+
+EXPORT_MAP := rte_node_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/log.c b/lib/librte_node/log.c
new file mode 100644
index 000000000..f035f91e8
--- /dev/null
+++ b/lib/librte_node/log.c
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "node_private.h"
+
+int rte_node_logtype;
+
+RTE_INIT(rte_node_init_log)
+{
+	rte_node_logtype = rte_log_register("lib.node");
+	if (rte_node_logtype >= 0)
+		rte_log_set_level(rte_node_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
new file mode 100644
index 000000000..a97813ad4
--- /dev/null
+++ b/lib/librte_node/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+sources = files('null.c', 'log.c')
+allow_experimental_apis = true
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
+deps += ['graph']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
new file mode 100644
index 000000000..f30902a94
--- /dev/null
+++ b/lib/librte_node/node_private.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __NODE_PRIVATE_H__
+#define __NODE_PRIVATE_H__
+
+#include <rte_common.h>
+#include <rte_log.h>
+
+extern int rte_node_logtype;
+#define NODE_LOG(level, node_name, ...)                                        \
+	rte_log(RTE_LOG_##level, rte_node_logtype,                             \
+		RTE_FMT("NODE %s: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",  \
+			node_name, __func__, __LINE__,                         \
+			RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define node_err(node_name, ...) NODE_LOG(ERR, node_name, __VA_ARGS__)
+#define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
+#define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
+
+#endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/null.c b/lib/librte_node/null.c
new file mode 100644
index 000000000..c7cd8b6df
--- /dev/null
+++ b/lib/librte_node/null.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_graph.h>
+
+static uint16_t
+null(struct rte_graph *graph, struct rte_node *node, void **objs,
+	uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(graph);
+
+	return nb_objs;
+}
+
+static struct rte_node_register null_node = {
+	.name = "null",
+	.process = null,
+};
+
+RTE_NODE_REGISTER(null_node);
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
new file mode 100644
index 000000000..f87163bb9
--- /dev/null
+++ b/lib/librte_node/rte_node_version.map
@@ -0,0 +1,6 @@
+EXPERIMENTAL {
+	global:
+
+	rte_node_logtype;
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index c43d86bb9..147129b0b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'graph', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'node', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
@@ -186,6 +186,9 @@ foreach l:libraries
 
 			dpdk_libraries = [shared_lib] + dpdk_libraries
 			dpdk_static_libraries = [static_lib] + dpdk_static_libraries
+			if libname == 'rte_node'
+				dpdk_graph_nodes = [static_lib]
+			endif
 		endif # sources.length() > 0
 
 		set_variable('shared_rte_' + name, shared_dep)
diff --git a/meson.build b/meson.build
index d36580438..269ba9614 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@ cc = meson.get_compiler('c')
 dpdk_conf = configuration_data()
 dpdk_libraries = []
 dpdk_static_libraries = []
+dpdk_graph_nodes = []
 dpdk_driver_classes = []
 dpdk_drivers = []
 dpdk_extra_ldflags = []
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index b1195f09a..68d7806a4 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -99,6 +99,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
 _LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
+_LDLIBS-$(CONFIG_RTE_LIBRTE_NODE)           += -lrte_node
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 16/29] node: add ethdev Rx node
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (14 preceding siblings ...)
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 15/29] node: add log infra and null node jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-09 23:05         ` Andrzej Ostruszka
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 17/29] node: add ethdev Tx node jerinj
                         ` (14 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add source rte_node ethdev_rx process function and register
it. This node is a source node that will be called periodically
and when called, performs rte_eth_rx_burst() on a specific
(port, queue) pair and enqueue them as stream of objects to
next node.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |   3 +-
 lib/librte_node/ethdev_rx.c      | 221 +++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_rx_priv.h |  81 +++++++++++
 lib/librte_node/meson.build      |   4 +-
 4 files changed, 306 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index dbc8e1d44..314149385 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,12 +11,13 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph
+LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_rx.c b/lib/librte_node/ethdev_rx.c
new file mode 100644
index 000000000..5cc736598
--- /dev/null
+++ b/lib/librte_node/ethdev_rx.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_rx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_rx_node_main ethdev_rx_main;
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process_inline(struct rte_graph *graph, struct rte_node *node,
+			      uint16_t port, uint16_t queue)
+{
+	uint16_t count, next_index = ETHDEV_RX_NEXT_IP4_LOOKUP;
+
+	/* Get pkts from port */
+	count = rte_eth_rx_burst(port, queue, (struct rte_mbuf **)node->objs,
+				 RTE_GRAPH_BURST_SIZE);
+
+	if (!count)
+		return 0;
+	node->idx = count;
+	/* Enqueue to next node */
+	rte_node_next_stream_move(graph, node, next_index);
+
+	return count;
+}
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t cnt)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	uint16_t n_pkts = 0;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(cnt);
+
+	n_pkts = ethdev_rx_node_process_inline(graph, node, ctx->port_id,
+					       ctx->queue_id);
+	return n_pkts;
+}
+
+static inline uint32_t
+l3_ptype(uint16_t etype, uint32_t ptype)
+{
+	ptype = ptype & ~RTE_PTYPE_L3_MASK;
+	if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
+		ptype |= RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
+	else if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6))
+		ptype |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	return ptype;
+}
+
+/* Callback for soft ptype parsing */
+static uint16_t
+eth_pkt_parse_cb(uint16_t port, uint16_t queue, struct rte_mbuf **mbufs,
+		 uint16_t nb_pkts, uint16_t max_pkts, void *user_param)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3;
+	struct rte_ether_hdr *eth_hdr;
+	uint16_t etype, n_left;
+	struct rte_mbuf **pkts;
+
+	RTE_SET_USED(port);
+	RTE_SET_USED(queue);
+	RTE_SET_USED(max_pkts);
+	RTE_SET_USED(user_param);
+
+	pkts = mbufs;
+	n_left = nb_pkts;
+	while (n_left >= 12) {
+
+		/* Prefetch next-next mbufs */
+		rte_prefetch0(pkts[8]);
+		rte_prefetch0(pkts[9]);
+		rte_prefetch0(pkts[10]);
+		rte_prefetch0(pkts[11]);
+
+		/* Prefetch next mbuf data */
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[4], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[5], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[6], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[7], struct rte_ether_hdr *));
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+		pkts += 4;
+		n_left -= 4;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf1 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf1->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf2 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf2->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf3 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf3->packet_type = l3_ptype(etype, 0);
+	}
+
+	while (n_left > 0) {
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left -= 1;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+	}
+
+	return nb_pkts;
+}
+
+#define MAX_PTYPES 16
+static int
+ethdev_ptype_setup(uint16_t port, uint16_t queue)
+{
+	uint8_t l3_ipv4 = 0, l3_ipv6 = 0;
+	uint32_t ptypes[MAX_PTYPES];
+	int i, rc;
+
+	/* Check IPv4 & IPv6 ptype support */
+	rc = rte_eth_dev_get_supported_ptypes(port, RTE_PTYPE_L3_MASK, ptypes,
+					      MAX_PTYPES);
+	for (i = 0; i < rc; i++) {
+		if (ptypes[i] & RTE_PTYPE_L3_IPV4)
+			l3_ipv4 = 1;
+		if (ptypes[i] & RTE_PTYPE_L3_IPV6)
+			l3_ipv6 = 1;
+	}
+
+	if (!l3_ipv4 || !l3_ipv6) {
+		node_info("ethdev_rx",
+			  "Enabling ptype callback for required ptypes on port %u\n",
+			  port);
+
+		if (!rte_eth_add_rx_callback(port, queue, eth_pkt_parse_cb,
+					     NULL)) {
+			node_err("ethdev_rx",
+				 "Failed to add rx ptype cb: port=%d, queue=%d\n",
+				 port, queue);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int
+ethdev_rx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	ethdev_rx_node_elem_t *elem = ethdev_rx_main.head;
+
+	RTE_SET_USED(graph);
+
+	while (elem) {
+		if (elem->nid == node->id) {
+			/* Update node specific context */
+			memcpy(ctx, &elem->ctx, sizeof(ethdev_rx_node_ctx_t));
+			break;
+		}
+		elem = elem->next;
+	}
+
+	RTE_VERIFY(elem != NULL);
+
+	/* Check and setup ptype */
+	return ethdev_ptype_setup(ctx->port_id, ctx->queue_id);
+}
+
+struct ethdev_rx_node_main *
+ethdev_rx_get_node_data_get(void)
+{
+	return &ethdev_rx_main;
+}
+
+static struct rte_node_register ethdev_rx_node_base = {
+	.process = ethdev_rx_node_process,
+	.flags = RTE_NODE_SOURCE_F,
+	.name = "ethdev_rx",
+
+	.init = ethdev_rx_node_init,
+
+	.nb_edges = ETHDEV_RX_NEXT_MAX,
+	.next_nodes = {[ETHDEV_RX_NEXT_IP4_LOOKUP] = "ip4_lookup"},
+};
+
+struct rte_node_register *
+ethdev_rx_node_get(void)
+{
+	return &ethdev_rx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_rx_node_base);
diff --git a/lib/librte_node/ethdev_rx_priv.h b/lib/librte_node/ethdev_rx_priv.h
new file mode 100644
index 000000000..2d7195a36
--- /dev/null
+++ b/lib/librte_node/ethdev_rx_priv.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_RX_PRIV_H__
+#define __INCLUDE_ETHDEV_RX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+struct ethdev_rx_node_elem;
+struct ethdev_rx_node_ctx;
+typedef struct ethdev_rx_node_elem ethdev_rx_node_elem_t;
+typedef struct ethdev_rx_node_ctx ethdev_rx_node_ctx_t;
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node context structure.
+ */
+struct ethdev_rx_node_ctx {
+	uint16_t port_id;  /**< Port identifier of the Rx node. */
+	uint16_t queue_id; /**< Queue identifier of the Rx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node list element structure.
+ */
+struct ethdev_rx_node_elem {
+	struct ethdev_rx_node_elem *next;
+	/**< Pointer to the next Rx node element. */
+	struct ethdev_rx_node_ctx ctx;
+	/**< Rx node context. */
+	rte_node_t nid;
+	/**< Node identifier of the Rx node. */
+};
+
+enum ethdev_rx_next_nodes {
+	ETHDEV_RX_NEXT_IP4_LOOKUP,
+	ETHDEV_RX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Rx node main structure.
+ */
+struct ethdev_rx_node_main {
+	ethdev_rx_node_elem_t *head;
+	/**< Pointer to the head Rx node element. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Rx node data.
+ */
+struct ethdev_rx_node_main *ethdev_rx_get_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Rx node.
+ */
+struct rte_node_register *ethdev_rx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_RX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index a97813ad4..94caa6c23 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph']
+deps += ['graph', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 17/29] node: add ethdev Tx node
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (15 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 16/29] node: add ethdev Rx node jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
                         ` (13 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add rte_node ethdev_tx process function and register it to
graph infra. This node has a specific (port, tx-queue) as context
and it enqueue's all the packets received to that specific queue pair.
When rte_eth_tx_burst() i.e enqueue to queue pair fails, packets
are forwarded to pkt_drop node to be free'd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |  3 +-
 lib/librte_node/ethdev_tx.c      | 86 ++++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_tx_priv.h | 62 +++++++++++++++++++++++
 lib/librte_node/meson.build      |  4 +-
 4 files changed, 152 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 314149385..7428f6c43 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
@@ -19,5 +19,6 @@ EXPORT_MAP := rte_node_version.map
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_tx.c b/lib/librte_node/ethdev_tx.c
new file mode 100644
index 000000000..075149089
--- /dev/null
+++ b/lib/librte_node/ethdev_tx.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_tx_priv.h"
+
+static struct ethdev_tx_node_main ethdev_tx_main;
+
+static uint16_t
+ethdev_tx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t nb_objs)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint16_t port, queue;
+	uint16_t count;
+
+	/* Get Tx port id */
+	port = ctx->port;
+	queue = ctx->queue;
+
+	count = rte_eth_tx_burst(port, queue, (struct rte_mbuf **)objs,
+				 nb_objs);
+
+	/* Redirect unsent pkts to drop node */
+	if (count != nb_objs) {
+		rte_node_enqueue(graph, node, ETHDEV_TX_NEXT_PKT_DROP,
+				 &objs[count], nb_objs - count);
+	}
+
+	return count;
+}
+
+static int
+ethdev_tx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint64_t port_id = RTE_MAX_ETHPORTS;
+	int i;
+
+	/* Find our port id */
+	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+		if (ethdev_tx_main.nodes[i] == node->id) {
+			port_id = i;
+			break;
+		}
+	}
+	RTE_VERIFY(port_id < RTE_MAX_ETHPORTS);
+
+	/* Update port and queue */
+	ctx->port = port_id;
+	ctx->queue = graph->id;
+
+	return 0;
+}
+
+struct ethdev_tx_node_main *
+ethdev_tx_node_data_get(void)
+{
+	return &ethdev_tx_main;
+}
+
+static struct rte_node_register ethdev_tx_node_base = {
+	.process = ethdev_tx_node_process,
+	.name = "ethdev_tx",
+
+	.init = ethdev_tx_node_init,
+
+	.nb_edges = ETHDEV_TX_NEXT_MAX,
+	.next_nodes = {
+		[ETHDEV_TX_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+struct rte_node_register *
+ethdev_tx_node_get(void)
+{
+	return &ethdev_tx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_tx_node_base);
diff --git a/lib/librte_node/ethdev_tx_priv.h b/lib/librte_node/ethdev_tx_priv.h
new file mode 100644
index 000000000..586bff44a
--- /dev/null
+++ b/lib/librte_node/ethdev_tx_priv.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_TX_PRIV_H__
+#define __INCLUDE_ETHDEV_TX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ethdev_tx_node_ctx;
+typedef struct ethdev_tx_node_ctx ethdev_tx_node_ctx_t;
+
+enum ethdev_tx_next_nodes {
+	ETHDEV_TX_NEXT_PKT_DROP,
+	ETHDEV_TX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node context structure.
+ */
+struct ethdev_tx_node_ctx {
+	uint16_t port;	/**< Port identifier of the Ethernet Tx node. */
+	uint16_t queue; /**< Queue identifier of the Ethernet Tx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node main structure.
+ */
+struct ethdev_tx_node_main {
+	uint32_t nodes[RTE_MAX_ETHPORTS]; /**< Tx nodes for each ethdev port. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Tx node data.
+ */
+struct ethdev_tx_node_main *ethdev_tx_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Tx node.
+ */
+struct rte_node_register *ethdev_tx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_TX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 94caa6c23..505c76abd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'ethdev']
+deps += ['graph', 'mbuf', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (16 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 17/29] node: add ethdev Tx node jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-09 23:07         ` Andrzej Ostruszka
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node jerinj
                         ` (12 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ctrl api to setup ethdev_rx and ethdev_tx node.
This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
nodes with specific (port, queue) pairs updated in their context.
All the ethdev ports and queues are setup before this api
is called.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 doc/api/doxy-api-index.md            |   2 +
 lib/librte_node/Makefile             |   6 +-
 lib/librte_node/ethdev_ctrl.c        | 100 +++++++++++++++++++++++++++
 lib/librte_node/meson.build          |   5 +-
 lib/librte_node/node_private.h       |  74 ++++++++++++++++++++
 lib/librte_node/rte_node_eth_api.h   |  70 +++++++++++++++++++
 lib/librte_node/rte_node_version.map |   1 +
 7 files changed, 255 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index fd2ff64d7..1784674df 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -161,6 +161,8 @@ The public API headers are grouped by topics:
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
     [graph_worker]     (@ref rte_graph_worker.h)
+  * graph_nodes:
+    [eth_node]         (@ref rte_node_eth_api.h),
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 7428f6c43..ea5fa77f7 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -20,5 +20,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
new file mode 100644
index 000000000..b2ac5e2c4
--- /dev/null
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+
+#include "rte_node_eth_api.h"
+
+#include "ethdev_rx_priv.h"
+#include "ethdev_tx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_ctrl {
+	uint16_t nb_graphs;
+} ctrl;
+
+int
+rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
+		    uint16_t nb_graphs)
+{
+	struct ethdev_tx_node_main *tx_node_data;
+	uint16_t tx_q_used, rx_q_used, port_id;
+	struct rte_node_register *tx_node;
+	char name[RTE_NODE_NAMESIZE];
+	struct rte_mempool *mp;
+	uint32_t id;
+	int i, j;
+
+	tx_node_data = ethdev_tx_node_data_get();
+	tx_node = ethdev_tx_node_get();
+	for (i = 0; i < nb_confs; i++) {
+		port_id = conf[i].port_id;
+
+		if (!rte_eth_dev_is_valid_port(port_id))
+			return -EINVAL;
+
+		/* Check for mbuf minimum private size requirement */
+		for (j = 0; j < conf[i].mp_count; j++) {
+			mp = conf[i].mp[j];
+			if (!mp)
+				continue;
+			/* Check for minimum private space */
+			if (rte_pktmbuf_priv_size(mp) <
+			    RTE_NODE_MBUF_PRIV2_SIZE) {
+				node_err("ethdev",
+					 "Minimum mbuf priv size requirement not met by mp %s",
+					 mp->name);
+				return -EINVAL;
+			}
+		}
+
+		rx_q_used = conf[i].num_rx_queues;
+		tx_q_used = conf[i].num_tx_queues;
+		/* Check if we have a txq for each worker */
+		if (tx_q_used < nb_graphs)
+			return -EINVAL;
+
+		/* Create node for each rx port queue pair */
+		for (j = 0; j < rx_q_used; j++) {
+			struct ethdev_rx_node_main *rx_node_data;
+			struct rte_node_register *rx_node;
+			ethdev_rx_node_elem_t *elem;
+
+			rx_node_data = ethdev_rx_get_node_data_get();
+			rx_node = ethdev_rx_node_get();
+			snprintf(name, sizeof(name), "%u-%u", port_id, j);
+			/* Clone a new rx node with same edges as parent */
+			id = rte_node_clone(rx_node->id, name);
+			if (id == RTE_NODE_ID_INVALID)
+				return -EIO;
+
+			/* Add it to list of ethdev rx nodes for lookup */
+			elem = malloc(sizeof(ethdev_rx_node_elem_t));
+			memset(elem, 0, sizeof(ethdev_rx_node_elem_t));
+			elem->ctx.port_id = port_id;
+			elem->ctx.queue_id = j;
+			elem->nid = id;
+			elem->next = rx_node_data->head;
+			rx_node_data->head = elem;
+
+			node_dbg("ethdev", "Rx node %s-%s: is at %u",
+				 rx_node->name, name, id);
+		}
+
+		/* Create a per port tx node from base node */
+		snprintf(name, sizeof(name), "%u", port_id);
+		/* Clone a new node with same edges as parent */
+		id = rte_node_clone(tx_node->id, name);
+		tx_node_data->nodes[port_id] = id;
+
+		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
+			 name, id);
+	}
+
+	ctrl.nb_graphs = nb_graphs;
+	return 0;
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 505c76abd..af2eb2d91 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
+headers = files('rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'mbuf', 'ethdev']
+deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
index f30902a94..141448546 100644
--- a/lib/librte_node/node_private.h
+++ b/lib/librte_node/node_private.h
@@ -6,7 +6,9 @@
 #define __NODE_PRIVATE_H__
 
 #include <rte_common.h>
+#include <rte_crypto.h>
 #include <rte_log.h>
+#include <rte_mbuf.h>
 
 extern int rte_node_logtype;
 #define NODE_LOG(level, node_name, ...)                                        \
@@ -19,4 +21,76 @@ extern int rte_node_logtype;
 #define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
 #define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store next hop, ttl and checksum.
+ */
+struct rte_node_mbuf_priv1 {
+	union {
+		/* IP4 rewrite */
+		struct {
+			uint16_t nh;
+			uint16_t ttl;
+			uint32_t cksum;
+		};
+
+		uint64_t u;
+	};
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Node mbuf private data to store crypto operation.
+ */
+struct rte_node_mbuf_priv2 {
+	union {
+		/* Sym crypto */
+		struct {
+			struct rte_crypto_op op;
+		};
+	};
+} __rte_cache_aligned;
+
+#define RTE_NODE_MBUF_PRIV2_SIZE sizeof(struct rte_node_mbuf_priv2)
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv1 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv1.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv1 *
+rte_node_mbuf_priv1(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv1 *)&m->udata64;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get mbuf_priv2 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv2.
+ */
+static __rte_always_inline struct rte_node_mbuf_priv2 *
+rte_node_mbuf_priv2(struct rte_mbuf *m)
+{
+	return (struct rte_node_mbuf_priv2 *)rte_mbuf_to_priv(m);
+}
+
 #endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/rte_node_eth_api.h b/lib/librte_node/rte_node_eth_api.h
new file mode 100644
index 000000000..39b31b45b
--- /dev/null
+++ b/lib/librte_node/rte_node_eth_api.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_ETH_API_H__
+#define __INCLUDE_RTE_NODE_ETH_API_H__
+
+/**
+ * @file rte_node_eth_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to setup ethdev_rx and ethdev_tx nodes
+ * and its queue associations.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_mempool.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Port config for ethdev_rx and ethdev_tx node.
+ */
+struct rte_node_ethdev_config {
+	uint16_t port_id;
+	/**< Port identifier */
+	uint16_t num_rx_queues;
+	/**< Number of Rx queues. */
+	uint16_t num_tx_queues;
+	/**< Number of Tx queues. */
+	struct rte_mempool **mp;
+	/**< Array of mempools associated to Rx queue. */
+	uint16_t mp_count;
+	/**< Size of mp array. */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Initializes ethdev nodes.
+ *
+ * @param cfg
+ *   Array of ethdev config that identifies which port's
+ *   ethdev_rx and ethdev_tx nodes need to be created
+ *   and queue association.
+ * @param cnt
+ *   Size of cfg array.
+ * @param nb_graphs
+ *   Number of graphs that will be used.
+ *
+ * @return
+ *   0 on successful initialization, negative otherwise.
+ */
+__rte_experimental
+int rte_node_eth_config(struct rte_node_ethdev_config *cfg,
+			uint16_t cnt, uint16_t nb_graphs);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_ETH_API_H__ */
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index f87163bb9..c6c71bd02 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -1,6 +1,7 @@
 EXPERIMENTAL {
 	global:
 
+	rte_node_eth_config;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 19/29] node: add generic ipv4 lookup node
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (17 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-09 23:07         ` Andrzej Ostruszka
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 20/29] node: ipv4 lookup for arm64 jerinj
                         ` (11 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup node.
This node performs LPM lookup using simple RTE_LPM API on every packet
received and forwards it to a next node that is identified by lookup
result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md          |   1 +
 lib/librte_node/Makefile           |   4 +-
 lib/librte_node/ip4_lookup.c       | 128 +++++++++++++++++++++++++++++
 lib/librte_node/meson.build        |   5 +-
 lib/librte_node/rte_node_ip4_api.h |  43 ++++++++++
 5 files changed, 178 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/rte_node_ip4_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 1784674df..3908fddde 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -163,6 +163,7 @@ The public API headers are grouped by topics:
     [graph_worker]     (@ref rte_graph_worker.h)
   * graph_nodes:
     [eth_node]         (@ref rte_node_eth_api.h),
+    [ip4_node]         (@ref rte_node_ip4_api.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ea5fa77f7..ad3f2e349 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_lpm -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -21,8 +21,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 
 # install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
new file mode 100644
index 000000000..a91d42423
--- /dev/null
+++ b/lib/librte_node/ip4_lookup.c
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_lpm.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "node_private.h"
+
+#define IPV4_L3FWD_LPM_MAX_RULES 1024
+#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
+
+/* IP4 Lookup global data struct */
+struct ip4_lookup_node_main {
+	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
+};
+
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	struct rte_mbuf *mbuf;
+	rte_edge_t next_index;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	int i, rc;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+	from = objs;
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	for (i = 0; i < nb_objs; i++) {
+		uint32_t next_hop;
+		uint16_t next;
+
+		mbuf = (struct rte_mbuf *)objs[i];
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
+				sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next = (uint16_t)next_hop;
+
+		if (unlikely(next_index != next)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+static int
+ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+
+	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_lookup_node = {
+	.process = ip4_lookup_node_process,
+	.name = "ip4_lookup",
+
+	.init = ip4_lookup_node_init,
+
+	.nb_edges = RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	.next_nodes = {
+		[RTE_NODE_IP4_LOOKUP_NEXT_REWRITE] = "ip4_rewrite",
+		[RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(ip4_lookup_node);
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index af2eb2d91..702d21a24 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
-headers = files('rte_node_eth_api.h')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
+		'ethdev_ctrl.c')
+headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
new file mode 100644
index 000000000..37c12bf82
--- /dev/null
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_IP4_API_H__
+#define __INCLUDE_RTE_NODE_IP4_API_H__
+
+/**
+ * @file rte_node_ip4_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to do control path functions of ip4_* nodes
+ * like ip4_lookup, ip4_rewrite.
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * IP4 lookup next nodes.
+ */
+enum rte_node_ip4_lookup_next {
+	RTE_NODE_IP4_LOOKUP_NEXT_REWRITE,
+	/**< Rewrite node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP,
+	/**< Packet drop node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	/**< Number of next nodes of lookup node. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_IP4_API_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 20/29] node: ipv4 lookup for arm64
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (18 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 21/29] node: ipv4 lookup for x86 jerinj
                         ` (10 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add arm64 specific IPv4 lookup process function
for ip4_lookup node. This node performs LPM lookup
on every packet received and forwards it to a next
node that is identified by lookup result.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_node/ip4_lookup.c      |   6 +
 lib/librte_node/ip4_lookup_neon.h | 238 ++++++++++++++++++++++++++++++
 2 files changed, 244 insertions(+)
 create mode 100644 lib/librte_node/ip4_lookup_neon.h

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index a91d42423..e8732e31b 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -28,6 +28,10 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+#if defined(RTE_MACHINE_CPUFLAG_NEON)
+#include "ip4_lookup_neon.h"
+#else
+
 static uint16_t
 ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 			void **objs, uint16_t nb_objs)
@@ -101,6 +105,8 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 	return nb_objs;
 }
 
+#endif
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_node/ip4_lookup_neon.h b/lib/librte_node/ip4_lookup_neon.h
new file mode 100644
index 000000000..0aafe4db2
--- /dev/null
+++ b/lib/librte_node/ip4_lookup_neon.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_IP4_LOOKUP_NEON_H__
+#define __INCLUDE_IP4_LOOKUP_NEON_H__
+
+/* ARM64 NEON */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	rte_edge_t next_index;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t result;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int32x4_t dip;
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+#define OBJS_PER_CLINE (RTE_CACHE_LINE_SIZE / sizeof(void *))
+	for (i = OBJS_PER_CLINE; i < RTE_GRAPH_BURST_SIZE; i += OBJS_PER_CLINE)
+		rte_prefetch0(&objs[i]);
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+#if RTE_GRAPH_BURST_SIZE > 64
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+#endif
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 0);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[1] = ipv4_hdr->time_to_live;
+		priv01.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 1);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[5] = ipv4_hdr->time_to_live;
+		priv01.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 2);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[1] = ipv4_hdr->time_to_live;
+		priv23.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 3);
+
+		dip = vreinterpretq_s32_u8(
+			vrev32q_u8(vreinterpretq_u8_s32(dip)));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[5] = ipv4_hdr->time_to_live;
+		priv23.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, result.u32, drop_nh);
+		priv01.u16[0] = result.u16[0];
+		priv01.u16[4] = result.u16[2];
+		priv23.u16[0] = result.u16[4];
+		priv23.u16[4] = result.u16[6];
+
+		rte_node_mbuf_priv1(mbuf0)->u = priv01.u64[0];
+		rte_node_mbuf_priv1(mbuf1)->u = priv01.u64[1];
+		rte_node_mbuf_priv1(mbuf2)->u = priv23.u64[0];
+		rte_node_mbuf_priv1(mbuf3)->u = priv23.u64[1];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec = ((next_index == result.u16[1]) &&
+				       (result.u16[1] == result.u16[3]) &&
+				       (result.u16[3] == result.u16[5]) &&
+				       (result.u16[5] == result.u16[7]));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == result.u16[1]) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[1],
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == result.u16[3]) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[3],
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == result.u16[5]) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[5],
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == result.u16[7]) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[7],
+						    from[3]);
+			}
+
+			from += 4;
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+		uint16_t next0;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next0 = (uint16_t)next_hop;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#endif /* __INCLUDE_IP4_LOOKUP_NEON_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 21/29] node: ipv4 lookup for x86
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (19 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 20/29] node: ipv4 lookup for arm64 jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 22/29] node: add ipv4 rewrite node jerinj
                         ` (9 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup
rte_node. This node performs LPM lookup using x86_64
vector supported RTE_LPM API on every packet received
and forwards it to a next node that is identified by
lookup result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ip4_lookup.c     |   2 +
 lib/librte_node/ip4_lookup_sse.h | 244 +++++++++++++++++++++++++++++++
 2 files changed, 246 insertions(+)
 create mode 100644 lib/librte_node/ip4_lookup_sse.h

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index e8732e31b..3a38f5ad8 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -30,6 +30,8 @@ struct ip4_lookup_node_main {
 
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 #include "ip4_lookup_neon.h"
+#elif defined(RTE_ARCH_X86)
+#include "ip4_lookup_sse.h"
 #else
 
 static uint16_t
diff --git a/lib/librte_node/ip4_lookup_sse.h b/lib/librte_node/ip4_lookup_sse.h
new file mode 100644
index 000000000..2efe48bf8
--- /dev/null
+++ b/lib/librte_node/ip4_lookup_sse.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_IP4_LOOKUP_SSE_H__
+#define __INCLUDE_IP4_LOOKUP_SSE_H__
+
+/* X86 SSE */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	rte_edge_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	uint32_t ip0, ip1, ip2, ip3;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t dst;
+	__m128i dip; /* SSE register */
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	if (n_left_from >= 4) {
+		for (i = 0; i < 4; i++)
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+	}
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip0 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip1 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip2 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip3 = ipv4_hdr->dst_addr;
+
+		/* Prepare for lookup x4 */
+		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
+
+		/* Byte swap 4 IPV4 addresses. */
+		const __m128i bswap_mask = _mm_set_epi8(
+			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
+		dip = _mm_shuffle_epi8(dip, bswap_mask);
+
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr->time_to_live;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
+
+		/* Extract next node id and NH */
+		rte_node_mbuf_priv1(mbuf0)->nh = dst.u32[0] & 0xFFFF;
+		next0 = (dst.u32[0] >> 16);
+
+		rte_node_mbuf_priv1(mbuf1)->nh = dst.u32[1] & 0xFFFF;
+		next1 = (dst.u32[1] >> 16);
+
+		rte_node_mbuf_priv1(mbuf2)->nh = dst.u32[2] & 0xFFFF;
+		next2 = (dst.u32[2] >> 16);
+
+		rte_node_mbuf_priv1(mbuf3)->nh = dst.u32[3] & 0xFFFF;
+		next3 = (dst.u32[3] >> 16);
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			(next_index ^ next0) | (next_index ^ next1) |
+			(next_index ^ next2) | (next_index ^ next3);
+
+		if (unlikely(fix_spec)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		rte_node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		rte_node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		rte_node_mbuf_priv1(mbuf0)->nh = next_hop & 0xFFFF;
+		next0 = (next_hop >> 16);
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	/* Copy things successfully speculated till now */
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#endif /* __INCLUDE_IP4_LOOKUP_SSE_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 22/29] node: add ipv4 rewrite node
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (20 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 21/29] node: ipv4 lookup for x86 jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
                         ` (8 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Kiran Kumar K <kirankumark@marvell.com>

Add ip4 rewrite process function for ip4_rewrite
rte_node. On every packet received by this node,
header is overwritten with new data before forwarding
it to next node. Header data to overwrite with is
identified by next hop id passed in mbuf priv data
by previous node.

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_node/Makefile           |   1 +
 lib/librte_node/ip4_rewrite.c      | 270 +++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h |  55 ++++++
 lib/librte_node/meson.build        |   2 +-
 4 files changed, 327 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ad3f2e349..ebf473c66 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -22,6 +22,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
new file mode 100644
index 000000000..ef49ccea0
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite.c
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_vect.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "ip4_rewrite_priv.h"
+#include "node_private.h"
+
+static struct ip4_rewrite_node_main *ip4_rewrite_nm;
+
+static uint16_t
+ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
+			 void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct ip4_rewrite_nh_header *nh = ip4_rewrite_nm->nh;
+	uint16_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ip0, *ip1, *ip2, *ip3;
+	uint16_t n_left_from, held = 0, last_spec = 0;
+	void *d0, *d1, *d2, *d3;
+	void **to_next, **from;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int i;
+
+	/* Speculative next as last next */
+	next_index = *(uint16_t *)node->ctx;
+	rte_prefetch0(nh);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(pkts[i]);
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	/* Update Ethernet header of pkts */
+	while (n_left_from >= 4) {
+		if (likely(n_left_from > 7)) {
+			/* Prefetch only next-mbuf struct and priv area.
+			 * Data need not be prefetched as we only write.
+			 */
+			rte_prefetch0(pkts[4]);
+			rte_prefetch0(pkts[5]);
+			rte_prefetch0(pkts[6]);
+			rte_prefetch0(pkts[7]);
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+		priv01.u64[0] = rte_node_mbuf_priv1(mbuf0)->u;
+		priv01.u64[1] = rte_node_mbuf_priv1(mbuf1)->u;
+		priv23.u64[0] = rte_node_mbuf_priv1(mbuf2)->u;
+		priv23.u64[1] = rte_node_mbuf_priv1(mbuf3)->u;
+
+		/* Increment checksum by one. */
+		priv01.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv01.u32[3] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[3] += rte_cpu_to_be_16(0x0100);
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf0 */
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[priv01.u16[0]].rewrite_data,
+			   nh[priv01.u16[0]].rewrite_len);
+
+		next0 = nh[priv01.u16[0]].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		ip0->time_to_live = priv01.u16[1] - 1;
+		ip0->hdr_checksum = priv01.u16[2] + priv01.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf1 */
+		d1 = rte_pktmbuf_mtod(mbuf1, void *);
+		rte_memcpy(d1, nh[priv01.u16[4]].rewrite_data,
+			   nh[priv01.u16[4]].rewrite_len);
+
+		next1 = nh[priv01.u16[4]].tx_node;
+		ip1 = (struct rte_ipv4_hdr *)((uint8_t *)d1 +
+					      sizeof(struct rte_ether_hdr));
+		ip1->time_to_live = priv01.u16[5] - 1;
+		ip1->hdr_checksum = priv01.u16[6] + priv01.u16[7];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf2 */
+		d2 = rte_pktmbuf_mtod(mbuf2, void *);
+		rte_memcpy(d2, nh[priv23.u16[0]].rewrite_data,
+			   nh[priv23.u16[0]].rewrite_len);
+		next2 = nh[priv23.u16[0]].tx_node;
+		ip2 = (struct rte_ipv4_hdr *)((uint8_t *)d2 +
+					      sizeof(struct rte_ether_hdr));
+		ip2->time_to_live = priv23.u16[1] - 1;
+		ip2->hdr_checksum = priv23.u16[2] + priv23.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf3 */
+		d3 = rte_pktmbuf_mtod(mbuf3, void *);
+		rte_memcpy(d3, nh[priv23.u16[4]].rewrite_data,
+			   nh[priv23.u16[4]].rewrite_len);
+
+		next3 = nh[priv23.u16[4]].tx_node;
+		ip3 = (struct rte_ipv4_hdr *)((uint8_t *)d3 +
+					      sizeof(struct rte_ether_hdr));
+		ip3->time_to_live = priv23.u16[5] - 1;
+		ip3->hdr_checksum = priv23.u16[6] + priv23.u16[7];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			((next_index == next0) && (next0 == next1) &&
+			 (next1 == next2) && (next2 == next3));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+			/* Change speculation if last two are same */
+			if ((next_index != next3) && (next2 == next3)) {
+				/* Put the current speculated node */
+				rte_node_next_stream_put(graph, node,
+							 next_index, held);
+				held = 0;
+
+				/* Get next speculated stream */
+				next_index = next3;
+				to_next = rte_node_next_stream_get(
+					graph, node, next_index, nb_objs);
+			}
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint16_t chksum;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_data,
+			   nh[rte_node_mbuf_priv1(mbuf0)->nh].rewrite_len);
+
+		next0 = nh[rte_node_mbuf_priv1(mbuf0)->nh].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		chksum = rte_node_mbuf_priv1(mbuf0)->cksum +
+			 rte_cpu_to_be_16(0x0100);
+		chksum += chksum >= 0xffff;
+		ip0->hdr_checksum = chksum;
+		ip0->time_to_live = rte_node_mbuf_priv1(mbuf0)->ttl - 1;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+	/* Save the last next used */
+	*(uint16_t *)node->ctx = next_index;
+
+	return nb_objs;
+}
+
+static int
+ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_rewrite_node = {
+	.process = ip4_rewrite_node_process,
+	.name = "ip4_rewrite",
+	/* Default edge i.e '0' is pkt drop */
+	.nb_edges = 1,
+	.next_nodes = {
+		[0] = "pkt_drop",
+	},
+	.init = ip4_rewrite_node_init,
+};
+
+RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
new file mode 100644
index 000000000..420996a03
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_IP4_REWRITE_PRIV_H__
+#define __INCLUDE_IP4_REWRITE_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+#define RTE_GRAPH_IP4_REWRITE_MAX_NH 64
+#define RTE_GRAPH_IP4_REWRITE_MAX_LEN 56
+
+/**
+ * @internal
+ *
+ * Ipv4 rewrite next hop header data structure. Used to store port specific
+ * rewrite data.
+ */
+struct ip4_rewrite_nh_header {
+	uint16_t rewrite_len; /**< Header rewrite length. */
+	uint16_t tx_node;     /**< Tx node next index identifier. */
+	uint16_t enabled;     /**< NH enable flag */
+	uint16_t rsvd;
+	union {
+		struct {
+			struct rte_ether_addr dst;
+			/**< Destination mac address. */
+			struct rte_ether_addr src;
+			/**< Source mac address. */
+		};
+		uint8_t rewrite_data[RTE_GRAPH_IP4_REWRITE_MAX_LEN];
+		/**< Generic rewrite data */
+	};
+};
+
+/**
+ * @internal
+ *
+ * Ipv4 node main data structure.
+ */
+struct ip4_rewrite_node_main {
+	struct ip4_rewrite_nh_header nh[RTE_GRAPH_IP4_REWRITE_MAX_NH];
+	/**< Array of next hop header data */
+	uint16_t next_index[RTE_MAX_ETHPORTS];
+	/**< Next index of each configured port. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_IP4_REWRITE_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 702d21a24..ed78791dd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 23/29] node: add ipv4 rewrite and lookup ctrl API
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (21 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 22/29] node: add ipv4 rewrite node jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-09 23:04         ` Andrzej Ostruszka
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 24/29] node: add packet drop node jerinj
                         ` (7 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ip4_rewrite and ip4_lookup ctrl API. ip4_lookup ctrl
API is used to add route entries for LPM lookup with
result data containing next hop id and next proto.
ip4_rewrite ctrl API is used to add rewrite data for
every next hop.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ethdev_ctrl.c        | 18 ++++++-
 lib/librte_node/ip4_lookup.c         | 80 ++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite.c        | 56 +++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h   | 22 ++++++++
 lib/librte_node/rte_node_ip4_api.h   | 44 +++++++++++++++
 lib/librte_node/rte_node_version.map |  2 +
 6 files changed, 221 insertions(+), 1 deletion(-)

diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
index b2ac5e2c4..845d92987 100644
--- a/lib/librte_node/ethdev_ctrl.c
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -11,6 +11,7 @@
 
 #include "ethdev_rx_priv.h"
 #include "ethdev_tx_priv.h"
+#include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
 static struct ethdev_ctrl {
@@ -21,14 +22,17 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 		    uint16_t nb_graphs)
 {
+	struct rte_node_register *ip4_rewrite_node;
 	struct ethdev_tx_node_main *tx_node_data;
 	uint16_t tx_q_used, rx_q_used, port_id;
 	struct rte_node_register *tx_node;
 	char name[RTE_NODE_NAMESIZE];
+	const char *next_nodes = name;
 	struct rte_mempool *mp;
+	int i, j, rc;
 	uint32_t id;
-	int i, j;
 
+	ip4_rewrite_node = ip4_rewrite_node_get();
 	tx_node_data = ethdev_tx_node_data_get();
 	tx_node = ethdev_tx_node_get();
 	for (i = 0; i < nb_confs; i++) {
@@ -93,6 +97,18 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 
 		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
 			 name, id);
+
+		/* Prepare the actual name of the cloned node */
+		snprintf(name, sizeof(name), "ethdev_tx-%u", port_id);
+
+		/* Add this tx port node as next to ip4_rewrite_node */
+		rte_node_edge_update(ip4_rewrite_node->id, RTE_EDGE_ID_INVALID,
+				     &next_nodes, 1);
+		/* Assuming edge id is the last one alloc'ed */
+		rc = ip4_rewrite_set_next(
+			port_id, rte_node_edge_count(ip4_rewrite_node->id) - 1);
+		if (rc < 0)
+			return rc;
 	}
 
 	ctrl.nb_graphs = nb_graphs;
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index 3a38f5ad8..d10d17879 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -28,6 +28,8 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+static struct ip4_lookup_node_main ip4_lookup_nm;
+
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 #include "ip4_lookup_neon.h"
 #elif defined(RTE_ARCH_X86)
@@ -109,12 +111,90 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 
 #endif
 
+int
+rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+		       enum rte_node_ip4_lookup_next next_node)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	struct in_addr in;
+	uint8_t socket;
+	uint32_t val;
+	int ret;
+
+	in.s_addr = htonl(ip);
+	inet_ntop(AF_INET, &in, abuf, sizeof(abuf));
+	/* Embedded next node id in next hop */
+	val = (next_node << 16) | next_hop;
+	node_dbg("ip4_lookup", "LPM: Adding route %s / %d nh (0x%x)", abuf,
+		 depth, val);
+
+	for (socket = 0; socket < RTE_MAX_NUMA_NODES; socket++) {
+		if (!ip4_lookup_nm.lpm_tbl[socket])
+			continue;
+
+		ret = rte_lpm_add(ip4_lookup_nm.lpm_tbl[socket], ip, depth,
+				  val);
+
+		if (ret < 0) {
+			node_err("ip4_lookup",
+				 "Unable to add entry %s / %d nh (%x) to LPM table on sock %d, rc=%d\n",
+				 abuf, depth, val, socket, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int
+setup_lpm(struct ip4_lookup_node_main *nm, int socket)
+{
+	struct rte_lpm_config config_ipv4;
+	char s[RTE_LPM_NAMESIZE];
+
+	/* One LPM table per socket */
+	if (nm->lpm_tbl[socket])
+		return 0;
+
+	/* create the LPM table */
+	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
+	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
+	config_ipv4.flags = 0;
+	snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socket);
+	nm->lpm_tbl[socket] = rte_lpm_create(s, socket, &config_ipv4);
+	if (nm->lpm_tbl[socket] == NULL)
+		return -rte_errno;
+
+	return 0;
+}
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
+	struct rte_lpm **lpm_p = (struct rte_lpm **)&node->ctx;
+	uint16_t socket, lcore_id;
+	static uint8_t init_once;
+	int rc;
+
 	RTE_SET_USED(graph);
 	RTE_SET_USED(node);
 
+	if (!init_once) {
+		/* Setup LPM tables for all sockets */
+		RTE_LCORE_FOREACH(lcore_id)
+		{
+			socket = rte_lcore_to_socket_id(lcore_id);
+			rc = setup_lpm(&ip4_lookup_nm, socket);
+			if (rc) {
+				node_err("ip4_lookup",
+					 "Failed to setup lpm tbl for sock %u, rc=%d",
+					 socket, rc);
+				return rc;
+			}
+		}
+		init_once = 1;
+	}
+	*lpm_p = ip4_lookup_nm.lpm_tbl[graph->socket];
 	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
 
 	return 0;
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
index ef49ccea0..5663f1eb1 100644
--- a/lib/librte_node/ip4_rewrite.c
+++ b/lib/librte_node/ip4_rewrite.c
@@ -256,6 +256,56 @@ ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 	return 0;
 }
 
+int
+ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index)
+{
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+	ip4_rewrite_nm->next_index[port_id] = next_index;
+
+	return 0;
+}
+
+int
+rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			 uint8_t rewrite_len, uint16_t dst_port)
+{
+	struct ip4_rewrite_nh_header *nh;
+
+	if (next_hop >= RTE_GRAPH_IP4_REWRITE_MAX_NH)
+		return -EINVAL;
+
+	if (rewrite_len > RTE_GRAPH_IP4_REWRITE_MAX_LEN)
+		return -EINVAL;
+
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+
+	/* Check if dst port doesn't exist as edge */
+	if (!ip4_rewrite_nm->next_index[dst_port])
+		return -EINVAL;
+
+	/* Update next hop */
+	nh = &ip4_rewrite_nm->nh[next_hop];
+
+	memcpy(nh->rewrite_data, rewrite_data, rewrite_len);
+	nh->tx_node = ip4_rewrite_nm->next_index[dst_port];
+	nh->rewrite_len = rewrite_len;
+	nh->enabled = true;
+
+	return 0;
+}
+
 static struct rte_node_register ip4_rewrite_node = {
 	.process = ip4_rewrite_node_process,
 	.name = "ip4_rewrite",
@@ -267,4 +317,10 @@ static struct rte_node_register ip4_rewrite_node = {
 	.init = ip4_rewrite_node_init,
 };
 
+struct rte_node_register *
+ip4_rewrite_node_get(void)
+{
+	return &ip4_rewrite_node;
+}
+
 RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
index 420996a03..80f0abdc9 100644
--- a/lib/librte_node/ip4_rewrite_priv.h
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -48,6 +48,28 @@ struct ip4_rewrite_node_main {
 	/**< Next index of each configured port. */
 };
 
+/**
+ * @internal
+ *
+ * Get the ipv4 rewrite node.
+ *
+ * @retrun
+ *   Pointer to the ipv4 rewrite node.
+ */
+struct rte_node_register *ip4_rewrite_node_get(void);
+
+/**
+ * @internal
+ *
+ * Set the Edge index of a given port_id.
+ *
+ * @param port_id
+ *   Ethernet port identifier.
+ * @param next_index
+ *   Edge index of the Given Tx node.
+ */
+int ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
index 37c12bf82..394cac097 100644
--- a/lib/librte_node/rte_node_ip4_api.h
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -36,6 +36,50 @@ enum rte_node_ip4_lookup_next {
 	/**< Number of next nodes of lookup node. */
 };
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add ipv4 route to lookup table.
+ *
+ * @param ip
+ *   IP address of route to be added.
+ * @param depth
+ *   Depth of the rule to be added.
+ * @param next_hop
+ *   Next hop id of the rule result to be added.
+ * @param next_node
+ *   Next node to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+			   enum rte_node_ip4_lookup_next next_node);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add a next hop's rewrite data.
+ *
+ * @param next_hop
+ *   Next hop id to add rewrite data to.
+ * @param rewrite_data
+ *   Rewrite data.
+ * @param rewrite_len
+ *   Length of rewrite data.
+ * @param dst_port
+ *   Destination port to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			     uint8_t rewrite_len, uint16_t dst_port);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index c6c71bd02..a799b0d38 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -2,6 +2,8 @@ EXPERIMENTAL {
 	global:
 
 	rte_node_eth_config;
+	rte_node_ip4_route_add;
+	rte_node_ip4_rewrite_add;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v4 24/29] node: add packet drop node
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (22 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
                         ` (6 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add packet drop node process function for pkt_drop
rte_node. This node simply free's every object received as
an rte_mbuf to its rte_pktmbuf pool.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile    |  1 +
 lib/librte_node/meson.build |  2 +-
 lib/librte_node/pkt_drop.c  | 26 ++++++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/pkt_drop.c

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ebf473c66..322b651c8 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -23,6 +23,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += pkt_drop.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index ed78791dd..8aa93654b 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ip4_rewrite.c', 'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'pkt_drop.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/librte_node/pkt_drop.c b/lib/librte_node/pkt_drop.c
new file mode 100644
index 000000000..c35001323
--- /dev/null
+++ b/lib/librte_node/pkt_drop.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_graph.h>
+#include <rte_mbuf.h>
+
+static uint16_t
+pkt_drop_process(struct rte_graph *graph, struct rte_node *node, void **objs,
+		 uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(graph);
+
+	rte_pktmbuf_free_bulk((struct rte_mbuf **)objs, nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register pkt_drop_node = {
+	.process = pkt_drop_process,
+	.name = "pkt_drop",
+};
+
+RTE_NODE_REGISTER(pkt_drop_node);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 25/29] l3fwd-graph: add graph based l3fwd skeleton
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (23 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 24/29] node: add packet drop node jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-09 23:04         ` Andrzej Ostruszka
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 26/29] l3fwd-graph: add ethdev configuration changes jerinj
                         ` (5 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Thomas Monjalon, Marko Kovacevic, Ori Kam, Bruce Richardson,
	Radu Nicolau, Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori,
	Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark, xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph based l3fwd application skeleton with cmdline
parsing support inline with normal l3fwd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                      |   3 +
 examples/Makefile                |   3 +
 examples/l3fwd-graph/Makefile    |  58 ++++
 examples/l3fwd-graph/main.c      | 525 +++++++++++++++++++++++++++++++
 examples/l3fwd-graph/meson.build |  13 +
 examples/meson.build             |   6 +-
 6 files changed, 606 insertions(+), 2 deletions(-)
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index 55fb9bbb0..de57f452b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1574,6 +1574,9 @@ F: doc/guides/sample_app_ug/l2_forward_event.rst
 F: examples/l3fwd/
 F: doc/guides/sample_app_ug/l3_forward.rst
 
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+F: examples/l3fwd-graph/
+
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
 
diff --git a/examples/Makefile b/examples/Makefile
index feff79784..b7e99a2f7 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -51,6 +51,9 @@ DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl
 ifeq ($(CONFIG_RTE_LIBRTE_LPM)$(CONFIG_RTE_LIBRTE_HASH),yy)
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH),y)
+DIRS-y += l3fwd-graph
+endif
 DIRS-y += link_status_interrupt
 DIRS-y += multi_process
 DIRS-y += ntb
diff --git a/examples/l3fwd-graph/Makefile b/examples/l3fwd-graph/Makefile
new file mode 100644
index 000000000..f3cf275ec
--- /dev/null
+++ b/examples/l3fwd-graph/Makefile
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# binary name
+APP = l3fwd-graph
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+# Build using pkg-config variables if possible
+ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)
+
+all: shared
+.PHONY: shared static
+shared: build/$(APP)-shared
+	ln -sf $(APP)-shared build/$(APP)
+static: build/$(APP)-static
+	ln -sf $(APP)-static build/$(APP)
+
+PKGCONF=pkg-config --define-prefix
+
+PC_FILE := $(shell $(PKGCONF) --path libdpdk)
+CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) -DALLOW_EXPERIMENTAL_API
+LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
+LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)
+
+build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
+
+build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
+
+build:
+	@mkdir -p $@
+
+.PHONY: clean
+clean:
+	rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
+	test -d build && rmdir -p build || true
+
+else # Build using legacy build system
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, detect a build directory, by looking for a path with a .config
+RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -O3 $(USER_FLAGS)
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+endif
diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
new file mode 100644
index 000000000..e0c6f42fa
--- /dev/null
+++ b/examples/l3fwd-graph/main.c
@@ -0,0 +1,525 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_branch_prediction.h>
+#include <rte_common.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_per_lcore.h>
+#include <rte_string_fns.h>
+#include <rte_vect.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
+
+/* Log type */
+#define RTE_LOGTYPE_L3FWD_GRAPH RTE_LOGTYPE_USER1
+
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 1024
+#define RTE_TEST_TX_DESC_DEFAULT 1024
+
+#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
+#define MAX_RX_QUEUE_PER_PORT 128
+
+#define MAX_RX_QUEUE_PER_LCORE 16
+
+#define MAX_LCORE_PARAMS 1024
+
+#define NB_SOCKETS 8
+
+/**< Ports set in promiscuous mode off by default. */
+static int promiscuous_on;
+
+static int numa_on = 1;	  /**< NUMA is enabled by default. */
+static int per_port_pool; /**< Use separate buffer pools per port; disabled */
+			  /**< by default */
+
+static volatile bool force_quit;
+
+/* Ethernet addresses of ports */
+static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+xmm_t val_eth[RTE_MAX_ETHPORTS];
+
+/* Mask of enabled ports */
+static uint32_t enabled_port_mask;
+
+struct lcore_rx_queue {
+	uint16_t port_id;
+	uint8_t queue_id;
+};
+
+/* Lcore conf */
+struct lcore_conf {
+	uint16_t n_rx_queue;
+	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+} __rte_cache_aligned;
+
+static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+
+struct lcore_params {
+	uint16_t port_id;
+	uint8_t queue_id;
+	uint8_t lcore_id;
+} __rte_cache_aligned;
+
+static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
+static struct lcore_params lcore_params_array_default[] = {
+	{0, 0, 2}, {0, 1, 2}, {0, 2, 2}, {1, 0, 2}, {1, 1, 2},
+	{1, 2, 2}, {2, 0, 2}, {3, 0, 3}, {3, 1, 3},
+};
+
+static struct lcore_params *lcore_params = lcore_params_array_default;
+static uint16_t nb_lcore_params = RTE_DIM(lcore_params_array_default);
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_RSS,
+		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+	},
+	.rx_adv_conf = {
+		.rss_conf = {
+				.rss_key = NULL,
+				.rss_hf = ETH_RSS_IP,
+		},
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static int
+check_lcore_params(void)
+{
+	uint8_t queue, lcore;
+	int socketid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		queue = lcore_params[i].queue_id;
+		if (queue >= MAX_RX_QUEUE_PER_PORT) {
+			printf("Invalid queue number: %hhu\n", queue);
+			return -1;
+		}
+		lcore = lcore_params[i].lcore_id;
+		if (!rte_lcore_is_enabled(lcore)) {
+			printf("Error: lcore %hhu is not enabled in lcore mask\n",
+			       lcore);
+			return -1;
+		}
+
+		if (lcore == rte_get_master_lcore()) {
+			printf("Error: lcore %u is master lcore\n", lcore);
+			return -1;
+		}
+		socketid = rte_lcore_to_socket_id(lcore);
+		if ((socketid != 0) && (numa_on == 0)) {
+			printf("Warning: lcore %hhu is on socket %d with numa off\n",
+			       lcore, socketid);
+		}
+	}
+
+	return 0;
+}
+
+static int
+check_port_config(void)
+{
+	uint16_t portid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		portid = lcore_params[i].port_id;
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("Port %u is not enabled in port mask\n", portid);
+			return -1;
+		}
+		if (!rte_eth_dev_is_valid_port(portid)) {
+			printf("Port %u is not present on the board\n", portid);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+init_lcore_rx_queues(void)
+{
+	uint16_t i, nb_rx_queue;
+	uint8_t lcore;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		lcore = lcore_params[i].lcore_id;
+		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
+			printf("Error: too many queues (%u) for lcore: %u\n",
+			       (unsigned int)nb_rx_queue + 1,
+			       (unsigned int)lcore);
+			return -1;
+		}
+
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+			lcore_params[i].port_id;
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+			lcore_params[i].queue_id;
+		lcore_conf[lcore].n_rx_queue++;
+	}
+
+	return 0;
+}
+
+/* Display usage */
+static void
+print_usage(const char *prgname)
+{
+	fprintf(stderr,
+		"%s [EAL options] --"
+		" -p PORTMASK"
+		" [-P]"
+		" --config (port,queue,lcore)[,(port,queue,lcore)]"
+		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
+		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
+		" [--no-numa]"
+		" [--per-port-pool]\n\n"
+
+		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
+		"  -P : Enable promiscuous mode\n"
+		"  --config (port,queue,lcore): Rx queue configuration\n"
+		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for "
+		"port X\n"
+		"  --enable-jumbo: Enable jumbo frames\n"
+		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"                 maximum packet length in decimal (64-9600)\n"
+		"  --no-numa: Disable numa awareness\n"
+		"  --per-port-pool: Use separate buffer pool per port\n\n",
+		prgname);
+}
+
+static int
+parse_max_pkt_len(const char *pktlen)
+{
+	unsigned long len;
+	char *end = NULL;
+
+	/* Parse decimal string */
+	len = strtoul(pktlen, &end, 10);
+	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (len == 0)
+		return -1;
+
+	return len;
+}
+
+static int
+parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_config(const char *q_arg)
+{
+	enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD };
+	unsigned long int_fld[_NUM_FLD];
+	const char *p, *p0 = q_arg;
+	char *str_fld[_NUM_FLD];
+	uint32_t size;
+	char s[256];
+	char *end;
+	int i;
+
+	nb_lcore_params = 0;
+
+	while ((p = strchr(p0, '(')) != NULL) {
+		++p;
+		p0 = strchr(p, ')');
+		if (p0 == NULL)
+			return -1;
+
+		size = p0 - p;
+		if (size >= sizeof(s))
+			return -1;
+
+		snprintf(s, sizeof(s), "%.*s", size, p);
+		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
+		    _NUM_FLD)
+			return -1;
+		for (i = 0; i < _NUM_FLD; i++) {
+			errno = 0;
+			int_fld[i] = strtoul(str_fld[i], &end, 0);
+			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
+				return -1;
+		}
+		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
+			printf("Exceeded max number of lcore params: %hu\n",
+			       nb_lcore_params);
+			return -1;
+		}
+		lcore_params_array[nb_lcore_params].port_id =
+			(uint8_t)int_fld[FLD_PORT];
+		lcore_params_array[nb_lcore_params].queue_id =
+			(uint8_t)int_fld[FLD_QUEUE];
+		lcore_params_array[nb_lcore_params].lcore_id =
+			(uint8_t)int_fld[FLD_LCORE];
+		++nb_lcore_params;
+	}
+	lcore_params = lcore_params_array;
+
+	return 0;
+}
+
+static void
+parse_eth_dest(const char *optarg)
+{
+	uint8_t c, *dest, peer_addr[6];
+	uint16_t portid;
+	char *port_end;
+
+	errno = 0;
+	portid = strtoul(optarg, &port_end, 10);
+	if (errno != 0 || port_end == optarg || *port_end++ != ',')
+		rte_exit(EXIT_FAILURE, "Invalid eth-dest: %s", optarg);
+	if (portid >= RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE,
+			 "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", portid,
+			 RTE_MAX_ETHPORTS);
+
+	if (cmdline_parse_etheraddr(NULL, port_end, &peer_addr,
+				    sizeof(peer_addr)) < 0)
+		rte_exit(EXIT_FAILURE, "Invalid ethernet address: %s\n",
+			 port_end);
+	dest = (uint8_t *)&dest_eth_addr[portid];
+	for (c = 0; c < 6; c++)
+		dest[c] = peer_addr[c];
+	*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+}
+
+#define MAX_JUMBO_PKT_LEN  9600
+#define MEMPOOL_CACHE_SIZE 256
+
+static const char short_options[] = "p:" /* portmask */
+				    "P"	 /* promiscuous */
+	;
+
+#define CMD_LINE_OPT_CONFIG	   "config"
+#define CMD_LINE_OPT_ETH_DEST	   "eth-dest"
+#define CMD_LINE_OPT_NO_NUMA	   "no-numa"
+#define CMD_LINE_OPT_ENABLE_JUMBO  "enable-jumbo"
+#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool"
+enum {
+	/* Long options mapped to a short option */
+
+	/* First long only option value must be >= 256, so that we won't
+	 * conflict with short options
+	 */
+	CMD_LINE_OPT_MIN_NUM = 256,
+	CMD_LINE_OPT_CONFIG_NUM,
+	CMD_LINE_OPT_ETH_DEST_NUM,
+	CMD_LINE_OPT_NO_NUMA_NUM,
+	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
+	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
+};
+
+static const struct option lgopts[] = {
+	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL},
+	{NULL, 0, 0, 0},
+};
+
+/*
+ * This expression is used to calculate the number of mbufs needed
+ * depending on user input, taking  into account memory for rx and
+ * tx hardware rings, cache per lcore and mtable per port per lcore.
+ * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum
+ * value of 8192
+ */
+#define NB_MBUF(nports)                                                        \
+	RTE_MAX((nports * nb_rx_queue * nb_rxd +                               \
+		 nports * nb_lcores * RTE_GRAPH_BURST_SIZE +                   \
+		 nports * n_tx_queue * nb_txd +                                \
+		 nb_lcores * MEMPOOL_CACHE_SIZE), 8192u)
+
+/* Parse the argument given in the command line of the application */
+static int
+parse_args(int argc, char **argv)
+{
+	char *prgname = argv[0];
+	int option_index;
+	char **argvopt;
+	int opt, ret;
+
+	argvopt = argv;
+
+	/* Error or normal output strings. */
+	while ((opt = getopt_long(argc, argvopt, short_options, lgopts,
+				  &option_index)) != EOF) {
+
+		switch (opt) {
+		/* Portmask */
+		case 'p':
+			enabled_port_mask = parse_portmask(optarg);
+			if (enabled_port_mask == 0) {
+				fprintf(stderr, "Invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case 'P':
+			promiscuous_on = 1;
+			break;
+
+		/* Long options */
+		case CMD_LINE_OPT_CONFIG_NUM:
+			ret = parse_config(optarg);
+			if (ret) {
+				fprintf(stderr, "Invalid config\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case CMD_LINE_OPT_ETH_DEST_NUM:
+			parse_eth_dest(optarg);
+			break;
+
+		case CMD_LINE_OPT_NO_NUMA_NUM:
+			numa_on = 0;
+			break;
+
+		case CMD_LINE_OPT_ENABLE_JUMBO_NUM: {
+			const struct option lenopts = {"max-pkt-len",
+						       required_argument, 0, 0};
+
+			port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME;
+			port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS;
+
+			/*
+			 * if no max-pkt-len set, use the default
+			 * value RTE_ETHER_MAX_LEN.
+			 */
+			if (getopt_long(argc, argvopt, "", &lenopts,
+					&option_index) == 0) {
+				ret = parse_max_pkt_len(optarg);
+				if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) {
+					fprintf(stderr, "Invalid maximum "
+							"packet length\n");
+					print_usage(prgname);
+					return -1;
+				}
+				port_conf.rxmode.max_rx_pkt_len = ret;
+			}
+			break;
+		}
+
+		case CMD_LINE_OPT_PARSE_PER_PORT_POOL:
+			printf("Per port buffer pool is enabled\n");
+			per_port_pool = 1;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind - 1] = prgname;
+	ret = optind - 1;
+	optind = 1; /* Reset getopt lib */
+
+	return ret;
+}
+
+static void
+signal_handler(int signum)
+{
+	if (signum == SIGINT || signum == SIGTERM) {
+		printf("\n\nSignal %d received, preparing to exit...\n",
+		       signum);
+		force_quit = true;
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	uint16_t portid;
+	int ret;
+
+	/* Init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+	argc -= ret;
+	argv += ret;
+
+	force_quit = false;
+	signal(SIGINT, signal_handler);
+	signal(SIGTERM, signal_handler);
+
+	/* Pre-init dst MACs for all ports to 02:00:00:00:00:xx */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		dest_eth_addr[portid] =
+			RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40);
+		*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+	}
+
+	/* Parse application arguments (after the EAL ones) */
+	ret = parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L3FWD_GRAPH parameters\n");
+
+	if (check_lcore_params() < 0)
+		rte_exit(EXIT_FAILURE, "check_lcore_params() failed\n");
+
+	ret = init_lcore_rx_queues();
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues() failed\n");
+
+	if (check_port_config() < 0)
+		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
+
+	printf("Bye...\n");
+
+	return ret;
+}
diff --git a/examples/l3fwd-graph/meson.build b/examples/l3fwd-graph/meson.build
new file mode 100644
index 000000000..a816bd890
--- /dev/null
+++ b/examples/l3fwd-graph/meson.build
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+deps += ['graph', 'eal', 'lpm', 'ethdev', 'node' ]
+sources = files(
+	'main.c'
+)
+allow_experimental_apis = true
diff --git a/examples/meson.build b/examples/meson.build
index 1f2b6f516..3b540012f 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -2,8 +2,10 @@
 # Copyright(c) 2017-2019 Intel Corporation
 
 driver_libs = []
+node_libs = []
 if get_option('default_library') == 'static'
 	driver_libs = dpdk_drivers
+	node_libs = dpdk_graph_nodes
 endif
 
 execinfo = cc.find_library('execinfo', required: false)
@@ -23,7 +25,7 @@ all_examples = [
 	'l2fwd', 'l2fwd-cat', 'l2fwd-event',
 	'l2fwd-crypto', 'l2fwd-jobstats',
 	'l2fwd-keepalive', 'l3fwd',
-	'l3fwd-acl', 'l3fwd-power',
+	'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph',
 	'link_status_interrupt',
 	'multi_process/client_server_mp/mp_client',
 	'multi_process/client_server_mp/mp_server',
@@ -99,7 +101,7 @@ foreach example: examples
 		endif
 		executable('dpdk-' + name, sources,
 			include_directories: includes,
-			link_whole: driver_libs,
+			link_whole: driver_libs + node_libs,
 			link_args: dpdk_extra_ldflags,
 			c_args: cflags,
 			dependencies: dep_objs)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 26/29] l3fwd-graph: add ethdev configuration changes
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (24 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 27/29] l3fwd-graph: add graph config and main loop jerinj
                         ` (4 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add changes to ethdev port and queue configuration based
on command line parameters for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 350 +++++++++++++++++++++++++++++++++++-
 1 file changed, 349 insertions(+), 1 deletion(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index e0c6f42fa..47d2f2ecb 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -20,8 +20,10 @@
 
 #include <rte_branch_prediction.h>
 #include <rte_common.h>
+#include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
 #include <rte_per_lcore.h>
@@ -49,6 +51,10 @@
 
 #define NB_SOCKETS 8
 
+/* Static global variables used within this file. */
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
@@ -60,6 +66,7 @@ static volatile bool force_quit;
 
 /* Ethernet addresses of ports */
 static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+static struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
 xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 /* Mask of enabled ports */
@@ -110,6 +117,8 @@ static struct rte_eth_conf port_conf = {
 	},
 };
 
+static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
+
 static int
 check_lcore_params(void)
 {
@@ -165,6 +174,27 @@ check_port_config(void)
 	return 0;
 }
 
+static uint8_t
+get_port_n_rx_queues(const uint16_t port)
+{
+	int queue = -1;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		if (lcore_params[i].port_id == port) {
+			if (lcore_params[i].queue_id == queue + 1)
+				queue = lcore_params[i].queue_id;
+			else
+				rte_exit(EXIT_FAILURE,
+					 "Queue ids of the port %d must be"
+					 " in sequence and must start with 0\n",
+					 lcore_params[i].port_id);
+		}
+	}
+
+	return (uint8_t)(++queue);
+}
+
 static int
 init_lcore_rx_queues(void)
 {
@@ -470,6 +500,120 @@ parse_args(int argc, char **argv)
 	return ret;
 }
 
+static void
+print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
+{
+	char buf[RTE_ETHER_ADDR_FMT_SIZE];
+	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
+	printf("%s%s", name, buf);
+}
+
+static int
+init_mem(uint16_t portid, uint32_t nb_mbuf)
+{
+	uint32_t lcore_id;
+	int socketid;
+	char s[64];
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		if (numa_on)
+			socketid = rte_lcore_to_socket_id(lcore_id);
+		else
+			socketid = 0;
+
+		if (socketid >= NB_SOCKETS) {
+			rte_exit(EXIT_FAILURE,
+				 "Socket %d of lcore %u is out of range %d\n",
+				 socketid, lcore_id, NB_SOCKETS);
+		}
+
+		if (pktmbuf_pool[portid][socketid] == NULL) {
+			snprintf(s, sizeof(s), "mbuf_pool_%d:%d", portid,
+				 socketid);
+			/* Create a pool with priv size of a cacheline */
+			pktmbuf_pool[portid][socketid] =
+				rte_pktmbuf_pool_create(
+					s, nb_mbuf, MEMPOOL_CACHE_SIZE,
+					RTE_CACHE_LINE_SIZE,
+					RTE_MBUF_DEFAULT_BUF_SIZE, socketid);
+			if (pktmbuf_pool[portid][socketid] == NULL)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init mbuf pool on socket %d\n",
+					 socketid);
+			else
+				printf("Allocated mbuf pool on socket %d\n",
+				       socketid);
+		}
+	}
+
+	return 0;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90  /* 9s (90 * 100ms) in total */
+	uint8_t count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+	uint16_t portid;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		if (force_quit)
+			return;
+		all_ports_up = 1;
+		RTE_ETH_FOREACH_DEV(portid)
+		{
+			if (force_quit)
+				return;
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(portid, &link);
+			/* Print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status)
+					printf("Port%d Link Up. Speed %u Mbps "
+					       "-%s\n",
+					       portid, link.link_speed,
+					       (link.link_duplex ==
+						ETH_LINK_FULL_DUPLEX)
+						       ? ("full-duplex")
+						       : ("half-duplex\n"));
+				else
+					printf("Port %d Link Down\n", portid);
+				continue;
+			}
+			/* Clear all_ports_up flag if any link down */
+			if (link.link_status == ETH_LINK_DOWN) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* After finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* Set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("Done\n");
+		}
+	}
+}
+
 static void
 signal_handler(int signum)
 {
@@ -483,7 +627,14 @@ signal_handler(int signum)
 int
 main(int argc, char **argv)
 {
-	uint16_t portid;
+	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_eth_dev_info dev_info;
+	uint32_t n_tx_queue, nb_lcores;
+	struct rte_eth_txconf *txconf;
+	uint16_t queueid, portid;
+	struct lcore_conf *qconf;
+	uint32_t lcore_id;
+	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -519,6 +670,203 @@ main(int argc, char **argv)
 	if (check_port_config() < 0)
 		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
 
+	nb_ports = rte_eth_dev_count_avail();
+	nb_lcores = rte_lcore_count();
+
+	/* Initialize all ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		struct rte_eth_conf local_port_conf = port_conf;
+
+		/* Skip ports that are not enabled */
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("\nSkipping disabled port %d\n", portid);
+			continue;
+		}
+
+		/* Init port */
+		printf("Initializing port %d ... ", portid);
+		fflush(stdout);
+
+		nb_rx_queue = get_port_n_rx_queues(portid);
+		n_tx_queue = nb_lcores;
+		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
+			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
+		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
+		       nb_rx_queue, n_tx_queue);
+
+		rte_eth_dev_info_get(portid, &dev_info);
+		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+			local_port_conf.txmode.offloads |=
+				DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
+			dev_info.flow_type_rss_offloads;
+		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
+		    port_conf.rx_adv_conf.rss_conf.rss_hf) {
+			printf("Port %u modified RSS hash function based on "
+			       "hardware support,"
+			       "requested:%#" PRIx64 " configured:%#" PRIx64
+			       "\n",
+			       portid, port_conf.rx_adv_conf.rss_conf.rss_hf,
+			       local_port_conf.rx_adv_conf.rss_conf.rss_hf);
+		}
+
+		ret = rte_eth_dev_configure(portid, nb_rx_queue,
+					    n_tx_queue, &local_port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot configure device: err=%d, port=%d\n",
+				 ret, portid);
+
+		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
+						       &nb_txd);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot adjust number of descriptors: err=%d, "
+				 "port=%d\n",
+				 ret, portid);
+
+		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
+		print_ethaddr(" Address:", &ports_eth_addr[portid]);
+		printf(", ");
+		print_ethaddr(
+			"Destination:",
+			(const struct rte_ether_addr *)&dest_eth_addr[portid]);
+		printf(", ");
+
+		/*
+		 * prepare src MACs for each port.
+		 */
+		rte_ether_addr_copy(
+			&ports_eth_addr[portid],
+			(struct rte_ether_addr *)(val_eth + portid) + 1);
+
+		/* Init memory */
+		if (!per_port_pool) {
+			/* portid = 0; this is *not* signifying the first port,
+			 * rather, it signifies that portid is ignored.
+			 */
+			ret = init_mem(0, NB_MBUF(nb_ports));
+		} else {
+			ret = init_mem(portid, NB_MBUF(1));
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "init_mem() failed\n");
+
+		/* Init one TX queue per couple (lcore,port) */
+		queueid = 0;
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			qconf = &lcore_conf[lcore_id];
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
+			fflush(stdout);
+
+			txconf = &dev_info.default_txconf;
+			txconf->offloads = local_port_conf.txmode.offloads;
+			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+						     socketid, txconf);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_tx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+			queueid++;
+		}
+
+		printf("\n");
+	}
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		qconf = &lcore_conf[lcore_id];
+		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
+		fflush(stdout);
+		/* Init RX queues */
+		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+			struct rte_eth_rxconf rxq_conf;
+
+			portid = qconf->rx_queue_list[queue].port_id;
+			queueid = qconf->rx_queue_list[queue].queue_id;
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
+			fflush(stdout);
+
+			rte_eth_dev_info_get(portid, &dev_info);
+			rxq_conf = dev_info.default_rxconf;
+			rxq_conf.offloads = port_conf.rxmode.offloads;
+			if (!per_port_pool)
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf, pktmbuf_pool[0][socketid]);
+			else
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf,
+					pktmbuf_pool[portid][socketid]);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_rx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+
+		}
+	}
+
+	printf("\n");
+
+	/* Start ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* Start device */
+		ret = rte_eth_dev_start(portid);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "rte_eth_dev_start: err=%d, port=%d\n", ret,
+				 portid);
+
+		/*
+		 * If enabled, put device in promiscuous mode.
+		 * This allows IO forwarding mode to forward packets
+		 * to itself through 2 cross-connected  ports of the
+		 * target machine.
+		 */
+		if (promiscuous_on)
+			rte_eth_promiscuous_enable(portid);
+	}
+
+	printf("\n");
+
+	check_all_ports_link_status(enabled_port_mask);
+
+	/* Stop ports */
+	RTE_ETH_FOREACH_DEV(portid) {
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("Closing port %d...", portid);
+		rte_eth_dev_stop(portid);
+		rte_eth_dev_close(portid);
+		printf(" Done\n");
+	}
 	printf("Bye...\n");
 
 	return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 27/29] l3fwd-graph: add graph config and main loop
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (25 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 26/29] l3fwd-graph: add ethdev configuration changes jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-09 23:04         ` Andrzej Ostruszka
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 28/29] doc: add graph library programmer's guide guide jerinj
                         ` (3 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph creation, configuration logic and graph main loop.
This graph main loop is run on every slave lcore and calls
rte_graph_walk() to walk over lcore specific rte_graph.
Master core accumulates and prints graph walk stats of all the
lcore's graph's.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 242 +++++++++++++++++++++++++++++++++++-
 1 file changed, 240 insertions(+), 2 deletions(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index 47d2f2ecb..28db947b5 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -23,9 +23,13 @@
 #include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_graph_worker.h>
+#include <rte_launch.h>
 #include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
+#include <rte_node_eth_api.h>
+#include <rte_node_ip4_api.h>
 #include <rte_per_lcore.h>
 #include <rte_string_fns.h>
 #include <rte_vect.h>
@@ -75,12 +79,17 @@ static uint32_t enabled_port_mask;
 struct lcore_rx_queue {
 	uint16_t port_id;
 	uint8_t queue_id;
+	char node_name[RTE_NODE_NAMESIZE];
 };
 
 /* Lcore conf */
 struct lcore_conf {
 	uint16_t n_rx_queue;
 	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+
+	struct rte_graph *graph;
+	char name[RTE_GRAPH_NAMESIZE];
+	rte_graph_t graph_id;
 } __rte_cache_aligned;
 
 static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
@@ -119,6 +128,25 @@ static struct rte_eth_conf port_conf = {
 
 static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
 
+static struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS];
+
+struct ipv4_l3fwd_lpm_route {
+	uint32_t ip;
+	uint8_t depth;
+	uint8_t if_out;
+};
+
+#define IPV4_L3FWD_LPM_NUM_ROUTES                                              \
+	(sizeof(ipv4_l3fwd_lpm_route_array) /                                  \
+	 sizeof(ipv4_l3fwd_lpm_route_array[0]))
+/* 198.18.0.0/16 are set aside for RFC2544 benchmarking. */
+static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
+	{RTE_IPV4(198, 18, 0, 0), 24, 0}, {RTE_IPV4(198, 18, 1, 0), 24, 1},
+	{RTE_IPV4(198, 18, 2, 0), 24, 2}, {RTE_IPV4(198, 18, 3, 0), 24, 3},
+	{RTE_IPV4(198, 18, 4, 0), 24, 4}, {RTE_IPV4(198, 18, 5, 0), 24, 5},
+	{RTE_IPV4(198, 18, 6, 0), 24, 6}, {RTE_IPV4(198, 18, 7, 0), 24, 7},
+};
+
 static int
 check_lcore_params(void)
 {
@@ -624,17 +652,87 @@ signal_handler(int signum)
 	}
 }
 
+static void
+print_stats(void)
+{
+	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+	const char clr[] = {27, '[', '2', 'J', '\0'};
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker_*";
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL)
+		rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
+
+	while (!force_quit) {
+		/* Clear screen and move to top left */
+		printf("%s%s", clr, topLeft);
+		rte_graph_cluster_stats_get(stats, 0);
+		rte_delay_ms(1E3);
+	}
+
+	rte_graph_cluster_stats_destroy(stats);
+}
+
+/* Main processing loop */
+static int
+graph_main_loop(void *conf)
+{
+	struct lcore_conf *qconf;
+	struct rte_graph *graph;
+	uint32_t lcore_id;
+
+	RTE_SET_USED(conf);
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_conf[lcore_id];
+	graph = qconf->graph;
+
+	if (!graph) {
+		RTE_LOG(INFO, L3FWD_GRAPH, "Lcore %u has nothing to do\n",
+			lcore_id);
+		return 0;
+	}
+
+	RTE_LOG(INFO, L3FWD_GRAPH,
+		"Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+		qconf->name, graph);
+
+	while (likely(!force_quit))
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
 int
 main(int argc, char **argv)
 {
+	uint8_t rewrite_data[2 * sizeof(struct rte_ether_addr)];
+	static const char *node_patterns[] = {
+		"ip4*",
+		"ethdev_tx-*",
+		"pkt_drop",
+	};
 	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_graph_param graph_conf;
 	struct rte_eth_dev_info dev_info;
+	uint32_t nb_ports, nb_conf = 0;
 	uint32_t n_tx_queue, nb_lcores;
 	struct rte_eth_txconf *txconf;
-	uint16_t queueid, portid;
+	uint16_t queueid, portid, i;
 	struct lcore_conf *qconf;
+	uint16_t nb_graphs = 0;
+	uint16_t nb_patterns;
+	uint8_t rewrite_len;
 	uint32_t lcore_id;
-	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -783,6 +881,18 @@ main(int argc, char **argv)
 			queueid++;
 		}
 
+		/* Setup ethdev node config */
+		ethdev_conf[nb_conf].port_id = portid;
+		ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+		ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+		if (!per_port_pool)
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+
+		else
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+		ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+		nb_conf++;
 		printf("\n");
 	}
 
@@ -826,11 +936,26 @@ main(int argc, char **argv)
 					 "port=%d\n",
 					 ret, portid);
 
+			/* Add this queue node to its graph */
+			snprintf(qconf->rx_queue_list[queue].node_name,
+				 RTE_NODE_NAMESIZE, "ethdev_rx-%u-%u", portid,
+				 queueid);
+		}
+
+		/* Alloc a graph to this lcore only if source exists  */
+		if (qconf->n_rx_queue) {
+			qconf->graph_id = nb_graphs;
+			nb_graphs++;
 		}
 	}
 
 	printf("\n");
 
+	/* Ethdev node config, skip rx queue mapping */
+	ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+	if (ret)
+		rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", ret);
+
 	/* Start ports */
 	RTE_ETH_FOREACH_DEV(portid)
 	{
@@ -858,6 +983,119 @@ main(int argc, char **argv)
 
 	check_all_ports_link_status(enabled_port_mask);
 
+	/* Graph Initialization */
+	memset(&graph_conf, 0, sizeof(graph_conf));
+	graph_conf.node_patterns = node_patterns;
+	nb_patterns = RTE_DIM(node_patterns);
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		rte_graph_t graph_id;
+		rte_edge_t i;
+
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		qconf = &lcore_conf[lcore_id];
+
+		/* Skip graph creation if no source exists */
+		if (!qconf->n_rx_queue)
+			continue;
+
+		/* Add rx node patterns of this lcore */
+		for (i = 0; i < qconf->n_rx_queue; i++) {
+			graph_conf.node_patterns[nb_patterns + i] =
+				qconf->rx_queue_list[i].node_name;
+		}
+
+		graph_conf.nb_node_patterns = nb_patterns + i;
+		graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+		snprintf(qconf->name, sizeof(qconf->name), "worker_%u",
+			 lcore_id);
+
+		graph_id = rte_graph_create(qconf->name, &graph_conf);
+		if (graph_id != qconf->graph_id)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_create(): graph_id=%d not "
+				 " as expected for lcore %u(%u\n",
+				 graph_id, lcore_id, qconf->graph_id);
+
+		qconf->graph = rte_graph_lookup(qconf->name);
+		if (!qconf->graph)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_lookup(): graph %s not found\n",
+				 qconf->name);
+	}
+
+	memset(&rewrite_data, 0, sizeof(rewrite_data));
+	rewrite_len = sizeof(rewrite_data);
+
+	/* Add route to ip4 graph infra */
+	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+		char route_str[INET6_ADDRSTRLEN * 4];
+		char abuf[INET6_ADDRSTRLEN];
+		struct in_addr in;
+		uint32_t dst_port;
+		uint16_t next_hop;
+
+		/* Skip unused ports */
+		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
+		     enabled_port_mask) == 0)
+			continue;
+
+		dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+		next_hop = i;
+
+		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
+		snprintf(route_str, sizeof(route_str), "%s / %d (%d)",
+			 inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
+			 ipv4_l3fwd_lpm_route_array[i].depth,
+			 ipv4_l3fwd_lpm_route_array[i].if_out);
+
+		ret = rte_node_ip4_route_add(
+			ipv4_l3fwd_lpm_route_array[i].ip,
+			ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+			RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add ip4 route %s to graph\n",
+				 route_str);
+
+		memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+		/* Add next hop for a given destination */
+		ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+					       rewrite_len, dst_port);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add next hop %u for "
+				 "route %s\n",
+				 next_hop, route_str);
+
+		RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+			route_str, next_hop);
+	}
+
+	/* Launch per-lcore init on every slave lcore */
+	rte_eal_mp_remote_launch(graph_main_loop, NULL, SKIP_MASTER);
+
+	/* Accumulate and print stats on master until exit */
+	if (rte_graph_has_stats_feature())
+		print_stats();
+
+	/* Wait for slave cores to exit */
+	ret = 0;
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		ret = rte_eal_wait_lcore(lcore_id);
+		/* Destroy graph */
+		rte_graph_destroy(lcore_conf[lcore_id].name);
+		if (ret < 0) {
+			ret = -1;
+			break;
+		}
+	}
+
 	/* Stop ports */
 	RTE_ETH_FOREACH_DEV(portid) {
 		if ((enabled_port_mask & (1 << portid)) == 0)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 28/29] doc: add graph library programmer's guide guide
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (26 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 27/29] l3fwd-graph: add graph config and main loop jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 29/29] doc: add l3fwd graph application user guide jerinj
                         ` (2 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Adding programmer's guide for Graph library and the inbuilt nodes.
This patch also updates the release note for the new libraries.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   24 +
 6 files changed, 5532 insertions(+)
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg

diff --git a/doc/guides/prog_guide/graph_lib.rst b/doc/guides/prog_guide/graph_lib.rst
new file mode 100644
index 000000000..17101b10a
--- /dev/null
+++ b/doc/guides/prog_guide/graph_lib.rst
@@ -0,0 +1,397 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+Graph Library and Inbuilt Nodes
+===============================
+
+Graph architecture abstracts the data processing functions as a ``node`` and
+``links`` them together to create a complex ``graph`` to enable reusable/modular
+data processing functions.
+
+The graph library provides API to enable graph framework operations such as
+create, lookup, dump and destroy on graph and node operations such as clone,
+edge update, and edge shrink, etc. The API also allows to create the stats
+cluster to monitor per graph and per node stats.
+
+Features
+--------
+
+Features of the Graph library are:
+
+- Nodes as plugins.
+- Support for out of tree nodes.
+- Inbuilt nodes for packet processing.
+- Multi-process support.
+- Low overhead graph walk and node enqueue.
+- Low overhead statistics collection infrastructure.
+- Support to export the graph as a Graphviz dot file. See ``rte_graph_export()``.
+- Allow having another graph walk implementation in the future by segregating
+  the fast path(``rte_graph_worker.h``) and slow path code.
+
+Advantages of Graph architecture
+--------------------------------
+
+- Memory latency is the enemy for high-speed packet processing, moving the
+  similar packet processing code to a node will reduce the I cache and D
+  caches misses.
+- Exploits the probability that most packets will follow the same nodes in the
+  graph.
+- Allow SIMD instructions for packet processing of the node.-
+- The modular scheme allows having reusable nodes for the consumers.
+- The modular scheme allows us to abstract the vendor HW specific
+  optimizations as a node.
+
+Performance tuning parameters
+-----------------------------
+
+- Test with various burst size values (256, 128, 64, 32) using
+  CONFIG_RTE_GRAPH_BURST_SIZE config option.
+  The testing shows, on x86 and arm64 servers, The sweet spot is 256 burst
+  size. While on arm64 embedded SoCs, it is either 64 or 128.
+- Disable node statistics (using ``CONFIG_RTE_LIBRTE_GRAPH_STATS`` config option)
+  if not needed.
+- Use arm64 optimized memory copy for arm64 architecture by
+  selecting ``CONFIG_RTE_ARCH_ARM64_MEMCPY``.
+
+Programming model
+-----------------
+
+Anatomy of Node:
+~~~~~~~~~~~~~~~~
+
+.. _figure_anatomy_of_a_node:
+
+.. figure:: img/anatomy_of_a_node.*
+
+The :numref:`figure_anatomy_of_a_node` diagram depicts the anatomy of a node.
+
+The node is the basic building block of the graph framework.
+
+A node consists of:
+
+process():
+^^^^^^^^^^
+
+The callback function will be invoked by worker thread using
+``rte_graph_walk()`` function when there is data to be processed by the node.
+A graph node process the function using ``process()`` and enqueue to next
+downstream node using ``rte_node_enqueue*()`` function.
+
+Context memory:
+^^^^^^^^^^^^^^^
+
+It is memory allocated by the library to store the node-specific context
+information. This memory will be used by process(), init(), fini() callbacks.
+
+init():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_create()`` on when
+a node gets attached to a graph.
+
+fini():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_destroy()`` on when a
+node gets detached to a graph.
+
+Node name:
+^^^^^^^^^^
+
+It is the name of the node. When a node registers to graph library, the library
+gives the ID as ``rte_node_t`` type. Both ID or Name shall be used lookup the
+node. ``rte_node_from_name()``, ``rte_node_id_to_name()`` are the node
+lookup functions.
+
+nb_edges:
+^^^^^^^^^
+
+The number of downstream nodes connected to this node. The ``next_nodes[]``
+stores the downstream nodes objects. ``rte_node_edge_update()`` and
+``rte_node_edge_shrink()`` functions shall be used to update the ``next_node[]``
+objects. Consumers of the node APIs are free to update the ``next_node[]``
+objects till ``rte_graph_create()`` invoked.
+
+next_node[]:
+^^^^^^^^^^^^
+
+The dynamic array to store the downstream nodes connected to this node. Downstream
+node should not be current node itself or a source node.
+
+Source node:
+^^^^^^^^^^^^
+
+Source nodes are static nodes created using ``RTE_NODE_REGISTER`` by passing
+``flags`` as ``RTE_NODE_SOURCE_F``.
+While performing the graph walk, the ``process()`` function of all the source
+nodes will be called first. So that these nodes can be used as input nodes for a graph.
+
+Node creation and registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Node implementer creates the node by implementing ops and attributes of
+  ``struct rte_node_register``.
+
+* The library registers the node by invoking RTE_NODE_REGISTER on library load
+  using the constructor scheme. The constructor scheme used here to support multi-process.
+
+Link the Nodes to create the graph topology
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. _figure_link_the_nodes:
+
+.. figure:: img/link_the_nodes.*
+
+The :numref:`figure_link_the_nodes` diagram shows a graph topology after
+linking the N nodes.
+
+Once nodes are available to the program, Application or node public API
+functions can links them together to create a complex packet processing graph.
+
+There are multiple different types of strategies to link the nodes.
+
+Method (a):
+^^^^^^^^^^^
+Provide the ``next_nodes[]`` at the node registration time. See  ``struct rte_node_register::nb_edges``.
+This is a use case to address the static node scheme where one knows upfront the
+``next_nodes[]`` of the node.
+
+Method (b):
+^^^^^^^^^^^
+Use ``rte_node_edge_get()``, ``rte_node_edge_update()``, ``rte_node_edge_shrink()``
+to update the ``next_nodes[]`` links for the node runtime but before graph create.
+
+Method (c):
+^^^^^^^^^^^
+Use ``rte_node_clone()`` to clone a already existing node, created using RTE_NODE_REGISTER.
+When ``rte_node_clone()`` invoked, The library, would clone all the attributes
+of the node and creates a new one. The name for cloned node shall be
+``"parent_node_name-user_provided_name"``.
+
+This method enables the use case of Rx and Tx nodes where multiple of those nodes
+need to be cloned based on the number of CPU available in the system.
+The cloned nodes will be identical, except the ``"context memory"``.
+Context memory will have information of port, queue pair in case of Rx and Tx
+ethdev nodes.
+
+Create the graph object
+~~~~~~~~~~~~~~~~~~~~~~~
+Now that the nodes are linked, Its time to create a graph by including
+the required nodes. The application can provide a set of node patterns to
+form a graph object. The ``famish()`` API used underneath for the pattern
+matching to include the required nodes. After the graph create any changes to
+nodes or graph is not allowed.
+
+The ``rte_graph_create()`` API shall be used to create the graph.
+
+Example of a graph object creation:
+
+.. code-block:: console
+
+   {"ethdev_rx-0-0", ip4*, ethdev_tx-*"}
+
+In the above example, A graph object will be created with ethdev Rx
+node of port 0 and queue 0, all ipv4* nodes in the system,
+and ethdev tx node of all ports.
+
+Multicore graph processing
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+In the current graph library implementation, specifically,
+``rte_graph_walk()`` and ``rte_node_enqueue*`` fast path API functions
+are designed to work on single-core to have better performance.
+The fast path API works on graph object, So the multi-core graph
+processing strategy would be to create graph object PER WORKER.
+
+In fast path
+~~~~~~~~~~~~
+Typical fast-path code looks like below, where the application
+gets the fast-path graph object using ``rte_graph_lookup()``
+on the worker thread and run the ``rte_graph_walk()`` in a tight loop.
+
+.. code-block:: c
+
+    struct rte_graph *graph = rte_graph_lookup("worker0");
+
+    while (!done) {
+        rte_graph_walk(graph);
+    }
+
+Context update when graph walk in action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The fast-path object for the node is ``struct rte_node``.
+
+It may be possible that in slow-path or after the graph walk-in action,
+the user needs to update the context of the node hence access to
+``struct rte_node *`` memory.
+
+``rte_graph_foreach_node()``, ``rte_graph_node_get()``,
+``rte_graph_node_get_by_name()`` APIs can be used to to get the
+``struct rte_node*``. ``rte_graph_foreach_node()`` iterator function works on
+``struct rte_graph *`` fast-path graph object while others works on graph ID or name.
+
+Get the node statistics using graph cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The user may need to know the aggregate stats of the node across
+multiple graph objects. Especially the situation where each graph object bound
+to a worker thread.
+
+Introduced a graph cluster object for statistics.
+``rte_graph_cluster_stats_create()`` API shall be used for creating a
+graph cluster with multiple graph objects and ``rte_graph_cluster_stats_get()``
+to get the aggregate node statistics.
+
+An example statistics output from ``rte_graph_cluster_stats_get()``
+
+.. code-block:: diff
+
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+    |Node     |calls      |objs         |realloc_count  |objs/call  |objs/sec(10E6) |cycles/call|
+    +---------------------+-------------+---------------+-----------+---------------+-----------+
+    |node0    |12977424   |3322220544   |5              |256.000    |3047.151872    |20.0000    |
+    |node1    |12977653   |3322279168   |0              |256.000    |3047.210496    |17.0000    |
+    |node2    |12977696   |3322290176   |0              |256.000    |3047.221504    |17.0000    |
+    |node3    |12977734   |3322299904   |0              |256.000    |3047.231232    |17.0000    |
+    |node4    |12977784   |3322312704   |1              |256.000    |3047.243776    |17.0000    |
+    |node5    |12977825   |3322323200   |0              |256.000    |3047.254528    |17.0000    |
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+
+Node writing guidelines
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``process()`` function of a node is the fast-path function and that needs
+to be written carefully to achieve max performance.
+
+Broadly speaking, there are two different types of nodes.
+
+Static nodes
+~~~~~~~~~~~~
+The first kind of nodes are those that have a fixed ``next_nodes[]`` for the
+complete burst (like ethdev_rx, ethdev_tx) and it is simple to write.
+``process()`` function can move the obj burst to the next node either using
+``rte_node_next_stream_move()`` or using ``rte_node_next_stream_get()`` and
+``rte_node_next_stream_put()``.
+
+Intermediate nodes
+~~~~~~~~~~~~~~~~~~
+The second kind of such node is ``intermediate nodes`` that decide what is the
+``next_node[]`` to send to on a per-packet basis. In these nodes,
+
+* Firstly, there has to be the best possible packet processing logic.
+
+* Secondly, each packet needs to be queued to its next node.
+
+This can be done using ``rte_node_enqueue_[x1|x2|x4]()`` APIs if
+they are to single next or ``rte_node_enqueue_next()`` that takes array of nexts.
+
+In scenario where multiple intermediate nodes are present but most of the time
+each node using the same next node for all its packets, the cost of moving every
+pointer from current node's stream to next node's stream could be avoided.
+This is called home run and ``rte_node_next_stream_move()`` could be used to
+just move stream from the current node to the next node with least number of cycles.
+Since this can be avoided only in the case where all the packets are destined
+to the same next node, node implementation should be also having worst-case
+handling where every packet could be going to different next node.
+
+Example of intermediate node implementation with home run:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1. Start with speculation that next_node = node->ctx.
+This could be the next_node application used in the previous function call of this node.
+
+2. Get the next_node stream array with required space using
+``rte_node_next_stream_get(next_node, space)``.
+
+3. while n_left_from > 0 (i.e packets left to be sent) prefetch next pkt_set
+and process current pkt_set to find their next node
+
+4. if all the next nodes of the current pkt_set match speculated next node,
+just count them as successfully speculated(``last_spec``) till now and
+continue the loop without actually moving them to the next node. else if there is
+a mismatch, copy all the pkt_set pointers that were ``last_spec`` and move the
+current pkt_set to their respective next's nodes using ``rte_enqueue_next_x1()``.
+Also, one of the next_node can be updated as speculated next_node if it is more
+probable. Finally, reset ``last_spec`` to zero.
+
+5. if n_left_from != 0 then goto 3) to process remaining packets.
+
+6. if last_spec == nb_objs, All the objects passed were successfully speculated
+to single next node. So, the current stream can be moved to next node using
+``rte_node_next_stream_move(node, next_node)``.
+This is the ``home run`` where memcpy of buffer pointers to next node is avoided.
+
+7. Update the ``node->ctx`` with more probable next node.
+
+Graph object memory layout
+--------------------------
+.. _figure_graph_mem_layout:
+
+.. figure:: img/graph_mem_layout.*
+
+The :numref:`figure_graph_mem_layout` diagram shows ``rte_graph`` object memory
+layout. Understanding the memory layout helps to debug the graph library and
+improve the performance if needed.
+
+Graph object consists of a header, circular buffer to store the pending
+stream when walking over the graph, and variable-length memory to store
+the ``rte_node`` objects.
+
+The graph_nodes_mem_create() creates and populate this memory. The functions
+such as ``rte_graph_walk()`` and ``rte_node_enqueue_*`` use this memory
+to enable fastpath services.
+
+Inbuilt Nodes
+-------------
+
+DPDK provides a set of nodes for data processing. The following section
+details the documentation for the same.
+
+ethdev_rx
+~~~~~~~~~
+This node does ``rte_eth_rx_burst()`` into stream buffer passed to it
+(src node stream) and does ``rte_node_next_stream_move()`` only when
+there are packets received. Each ``rte_node`` works only on one Rx port and
+queue that it gets from node->ctx. For each (port X, rx_queue Y),
+a rte_node is cloned from  ethdev_rx_base_node as ``ethdev_rx-X-Y`` in
+``rte_node_eth_config()`` along with updating ``node->ctx``.
+Each graph needs to be associated  with a unique rte_node for a (port, rx_queue).
+
+ethdev_tx
+~~~~~~~~~
+This node does ``rte_eth_tx_burst()`` for a burst of objs received by it.
+It sends the burst to a fixed Tx Port and Queue information from
+node->ctx. For each (port X), this ``rte_node`` is cloned from
+ethdev_tx_node_base as "ethdev_tx-X" in ``rte_node_eth_config()``
+along with updating node->context.
+
+Since each graph doesn't need more than one Txq, per port, a Txq is assigned
+based on graph id to each rte_node instance. Each graph needs to be associated
+with a rte_node for each (port).
+
+pkt_drop
+~~~~~~~~
+This node frees all the objects passed to it considering them as
+``rte_mbufs`` that need to be freed.
+
+ip4_lookup
+~~~~~~~~~~
+This node is an intermediate node that does LPM lookup for the received
+ipv4 packets and the result determines each packets next node.
+
+On successful LPM lookup, the result contains the ``next_node`` id and
+``next-hop`` id with which the packet needs to be further processed.
+
+On LPM lookup failure, objects are redirected to pkt_drop node.
+``rte_node_ip4_route_add()`` is control path API to add ipv4 routes.
+To achieve home run, node use ``rte_node_stream_move()`` as mentioned in above
+sections.
+
+ip4_rewrite
+~~~~~~~~~~~
+This node gets packets from ``ip4_lookup`` node with next-hop id for each
+packet is embedded in ``rte_node_mbuf_priv1(mbuf)->nh``. This id is used
+to determine the L2 header to be written to the packet before sending
+the packet out to a particular ethdev_tx node.
+``rte_node_ip4_rewrite_add()`` is control path API to add next-hop info.
+
+null
+~~~~
+This node ignores the set of objects passed to it and reports that all are
+processed.
+
diff --git a/doc/guides/prog_guide/img/anatomy_of_a_node.svg b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
new file mode 100644
index 000000000..fa4b5b2d5
--- /dev/null
+++ b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
@@ -0,0 +1,1078 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg419"
+   sodipodi:docname="anatomy_of_a_node.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata425">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs423" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview421"
+     showgrid="false"
+     inkscape:zoom="2.406782"
+     inkscape:cx="470.64353"
+     inkscape:cy="284.06748"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg419" />
+  <clipPath
+     id="p.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path10" />
+  </clipPath>
+  <g
+     clip-path="url(#p.0)"
+     id="g417"
+     transform="matrix(1.0160138,0,0,1.0169275,-5.7394334,-5.6337913)">
+    <path
+       d="M 0,0 H 960 V 540 H 0 Z"
+       id="path13"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path15"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path17"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#741b47;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path19"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path21"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#c27ba0;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:4, 3, 1, 3" />
+    <path
+       d="M 540.8504,238.14069 V 415.12232"
+       id="path23"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="M 619.6588,238.14069 V 415.12232"
+       id="path25"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,238.63937 h 79.80579"
+       id="path27"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,273.8362 h 79.80579"
+       id="path29"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,309.03308 h 79.80579"
+       id="path31"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,344.22992 h 79.80579"
+       id="path33"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,379.42676 h 79.80579"
+       id="path35"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,414.62363 h 79.80579"
+       id="path37"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 559.7812,260.43936 v -9.54686 h 1.29687 l 5.01563,7.49998 v -7.49998 h 1.20312 v 9.54686 h -1.29687 l -5.01563,-7.49998 v 7.49998 z m 9.04706,-3.45312 q 0,-1.92186 1.07812,-2.84374 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57811 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28123 -0.59375,-1.93748 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98436 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03124 0.34375,-1.87499 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.
 79687,0.67188 v -3.42188 h 1.17188 v 9.54686 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42186 -0.54687,-2.07811 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,1.99999 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.74999 0.89063,-2.70311 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65623 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85936 -0.4375,-1.29686 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.43748 z m 9.89667,-0.57811 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.68
 75,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.29688 0.95313,0.89063 0.375,0.57812 0.59375,1.42187 0.21875,0.82813 0.21875,2.25 0,1.67186 -0.35938,2.70311 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.98438 -0.95313,-1.1875 -0.95313,-3.87498 z m 1.20313,0 q 0,2.34373 0.54687,3.12498 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.12498 0,-2.35937 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path39"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,295.63623 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.
 42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 H 598.128 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.1406
 2 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path41"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,330.83307 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29687 q -0.0156,-0.42187 0.14062,-0.8125 0.23438,-0.64062 0.76563,-1.26562 0.53125,-0.625
  1.53125,-1.45313 1.5625,-1.26562 2.10937,-2.01562 0.54688,-0.75 0.54688,-1.40625 0,-0.70313 -0.5,-1.17188 -0.5,-0.48437 -1.29688,-0.48437 -0.85937,0 -1.375,0.51562 -0.5,0.5 -0.5,1.39063 l -1.20312,-0.10938 q 0.125,-1.35937 0.92187,-2.0625 0.8125,-0.70312 2.17188,-0.70312 1.375,0 2.17187,0.76562 0.8125,0.75 0.8125,1.875 0,0.57813 -0.23437,1.14063 -0.23438,0.54687 -0.78125,1.15625 -0.54688,0.60937 -1.8125,1.67187 -1.04688,0.89063 -1.35938,1.21875 -0.29687,0.3125 -0.48437,0.625 z"
+       id="path43"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 577.7547,366.0299 v -1.32813 h 1.34375 v 1.32813 z m 3.703,0 v -1.32813 h 1.34375 v 1.32813 z"
+       id="path45"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,401.22678 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98438 q 0.75,-1.14063 2.1875,-1.14063 0.625,0 1.15625,0.21875 0.53125,0.218
 75 0.78125,0.59375 0.26562,0.35938 0.375,0.85938 0.0625,0.32812 0.0625,1.14062 v 4.25 h -1.17188 v -4.20312 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35938 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79687 v 3.78125 z"
+       id="path47"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path49"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path51"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 598.25494,126.88399 v -9.54688 h 1.29688 l 5.01562,7.5 v -7.5 h 1.20313 v 9.54688 h -1.29688 l -5.01562,-7.5 v 7.5 z m 9.047,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.4
 2188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z"
+       id="path53"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 596.41,142.88399 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 14.00012,-0.85938 q -0.65625,0.5625 -1.26562,0.79688 -0.59375,0.21875 -1.28125,0.21875 -1.14063,0 -1.75,-0.54688 -0.60938,-0.5625 -0.60938,-1.4375 0,-0.5 0.21875,-0.92187 0.23438,-0.42188 0.60938,-0.67188 0.375,-0.25 0.84375,-0.39062 0.34375,-0.0781 1.04687,-0.17188 1.42188,-0.17187 2.09375,-0.40625 0,-0.23437 0,-0.29687 0,-0.71875 -0.32812,-1.01563 -0.45313,-0.39062 -1.34375,-0.39062 -0.8125,0 -1.21875,0.29687 -0.39063,0.28125 -0.57813,1.01563 l -1.14062,-0.15625 q 0.15625,-0.73438 0.51562,-1.1875 0.35938,-0.45313 1.03125,-0.6875 0.67188,-0.25 1.5625,-0.25 0.89063,0 1.4375,0.20312 0.5625,0.20313 0.8125,0.53125 0.26563,0.3125 0.375,0.79688 0.0469,0.29687 0.0469,1.07812 v 1.5625 q 0,1.625 0.0781,2.0625 0.0781,0.4375 0.29688,0.82813 h -1.21875 q -0.1875,-0.35938 -0.23438,-0.85938 z m -0.0937,-2.60937 q -0.64062,0.26562 -1.92187,0.4375 -0.71875,0.10937
  -1.01563,0.25 -0.29687,0.125 -0.46875,0.375 -0.15625,0.25 -0.15625,0.54687 0,0.46875 0.34375,0.78125 0.35938,0.3125 1.04688,0.3125 0.67187,0 1.20312,-0.29687 0.53125,-0.29688 0.78125,-0.8125 0.1875,-0.39063 0.1875,-1.17188 z m 2.9906,3.46875 v -6.90625 h 1.04688 v 0.96875 q 0.32812,-0.51563 0.85937,-0.8125 0.54688,-0.3125 1.23438,-0.3125 0.78125,0 1.26562,0.3125 0.48438,0.3125 0.6875,0.89062 0.82813,-1.20312 2.14063,-1.20312 1.03125,0 1.57812,0.57812 0.5625,0.5625 0.5625,1.73438 v 4.75 h -1.17187 v -4.35938 q 0,-0.70312 -0.125,-1 -0.10938,-0.3125 -0.40625,-0.5 -0.29688,-0.1875 -0.70313,-0.1875 -0.71875,0 -1.20312,0.48438 -0.48438,0.48437 -0.48438,1.54687 v 4.01563 h -1.17187 v -4.48438 q 0,-0.78125 -0.29688,-1.17187 -0.28125,-0.39063 -0.92187,-0.39063 -0.5,0 -0.92188,0.26563 -0.42187,0.25 -0.60937,0.75 -0.1875,0.5 -0.1875,1.45312 v 3.57813 z m 15.83686,-2.21875 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0
 .89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z"
+       id="path55"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path57"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path59"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 186.03761,297.92676 v -11.48438 h 1.28125 v 1.07813 q 0.45312,-0.64063 1.01562,-0.95313 0.57813,-0.3125 1.39063,-0.3125 1.0625,0 1.875,0.54688 0.8125,0.54687 1.21875,1.54687 0.42187,0.98438 0.42187,2.17188 0,1.28125 -0.46875,2.29687 -0.45312,1.01563 -1.32812,1.5625 -0.85938,0.54688 -1.82813,0.54688 -0.70312,0 -1.26562,-0.29688 -0.54688,-0.29687 -0.90625,-0.75 v 4.04688 z m 1.26562,-7.29688 q 0,1.60938 0.64063,2.375 0.65625,0.76563 1.57812,0.76563 0.9375,0 1.60938,-0.79688 0.67187,-0.79687 0.67187,-2.45312 0,-1.59375 -0.65625,-2.375 -0.65625,-0.79688 -1.5625,-0.79688 -0.89062,0 -1.59375,0.84375 -0.6875,0.84375 -0.6875,2.4375 z m 7.61719,4.10938 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 4.8125,-4.15625 q 0,-2.29688 1.28125,-3.
 40625 1.07813,-0.92188 2.60938,-0.92188 1.71875,0 2.79687,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48437,2.51563 -0.48438,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73438,0 -2.8125,-1.10938 -1.07813,-1.125 -1.07813,-3.23437 z m 1.45313,0 q 0,1.59375 0.6875,2.39062 0.70312,0.79688 1.75,0.79688 1.04687,0 1.73437,-0.79688 0.70313,-0.79687 0.70313,-2.4375 0,-1.53125 -0.70313,-2.32812 -0.6875,-0.79688 -1.73437,-0.79688 -1.04688,0 -1.75,0.79688 -0.6875,0.78125 -0.6875,2.375 z m 13.38281,1.10937 1.39062,0.1875 q -0.23437,1.42188 -1.17187,2.23438 -0.92188,0.8125 -2.28125,0.8125 -1.70313,0 -2.75,-1.10938 -1.03125,-1.125 -1.03125,-3.20312 0,-1.34375 0.4375,-2.34375 0.45312,-1.01563 1.35937,-1.51563 0.92188,-0.5 1.98438,-0.5 1.35937,0 2.21875,0.6875 0.85937,0.67188 1.09375,1.9375 l -1.35938,0.20313 q -0.20312,-0.82813 -0.70312,-1.25 -0.48438,-0.42188 -1.1875,-0.42188 -1.0625,0 -1.73438,0.76563 -0.65625,0.75 -0.65625,2.40625 0,1.67187 0.64063,2.4375 0.64062,0.75 1.67187,0.
 75 0.82813,0 1.375,-0.5 0.5625,-0.51563 0.70313,-1.57813 z m 8.26562,0.375 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.27344,2.46875 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 
 0.25,-0.46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 8,0 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 0.25,-0.
 46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 11.25,5.85938 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375
 ,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path61"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path63"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path65"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 372.79996,224.29451 1.51562,0.375 q -0.46875,1.875 -1.71875,2.85937 -1.23437,0.98438 -3.01562,0.98438 -1.85938,0 -3.01563,-0.75 -1.15625,-0.76563 -1.76562,-2.1875 -0.60938,-1.4375 -0.60938,-3.07813 0,-1.79687 0.6875,-3.125 0.6875,-1.32812 1.9375,-2.01562 1.26563,-0.70313 2.78125,-0.70313 1.71875,0 2.89063,0.875 1.17187,0.875 1.64062,2.46875 l -1.5,0.34375 q -0.39062,-1.25 -1.15625,-1.8125 -0.75,-0.57812 -1.90625,-0.57812 -1.3125,0 -2.20312,0.64062 -0.89063,0.625 -1.25,1.70313 -0.35938,1.0625 -0.35938,2.1875 0,1.46875 0.42188,2.5625 0.4375,1.07812 1.32812,1.625 0.90625,0.53125 1.95313,0.53125 1.26562,0 2.14062,-0.73438 0.89063,-0.73437 1.20313,-2.17187 z m 2.67969,-0.14063 q 0,-2.29687 1.28125,-3.40625 1.07812,-0.92187 2.60937,-0.92187 1.71875,0 2.79688,1.125 1.09375,1.10937 1.09375,3.09375 0,1.59375 -0.48438,2.51562 -0.48437,0.92188 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10937 -1.07812,-1.125 -1.07812,-3.23438 z m 1.45312,0 q 0,1.59375 0.6875,2
 .39063 0.70313,0.79687 1.75,0.79687 1.04688,0 1.73438,-0.79687 0.70312,-0.79688 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32813 -0.6875,-0.79687 -1.73438,-0.79687 -1.04687,0 -1.75,0.79687 -0.6875,0.78125 -0.6875,2.375 z m 7.97656,4.15625 v -8.29687 h 1.26563 v 1.17187 q 0.90625,-1.35937 2.64062,-1.35937 0.75,0 1.375,0.26562 0.625,0.26563 0.9375,0.70313 0.3125,0.4375 0.4375,1.04687 0.0781,0.39063 0.0781,1.35938 v 5.10937 h -1.40625 v -5.04687 q 0,-0.85938 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57812 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 11.96094,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.59375,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 
 0.60937,-0.0625 z m 7.05469,-1.40625 1.45312,0.17187 q -0.34375,1.28125 -1.28125,1.98438 -0.92187,0.70312 -2.35937,0.70312 -1.82813,0 -2.89063,-1.125 -1.0625,-1.125 -1.0625,-3.14062 0,-2.09375 1.07813,-3.25 1.07812,-1.15625 2.79687,-1.15625 1.65625,0 2.70313,1.14062 1.0625,1.125 1.0625,3.17188 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76562,2.10937 0.70313,0.71875 1.73438,0.71875 0.78125,0 1.32812,-0.40625 0.54688,-0.40625 0.85938,-1.29687 z m -4.60938,-2.28125 h 4.625 q -0.0937,-1.04688 -0.53125,-1.5625 -0.67187,-0.8125 -1.73437,-0.8125 -0.96875,0 -1.64063,0.65625 -0.65625,0.64062 -0.71875,1.71875 z m 6.89844,4.95312 3.03125,-4.3125 -2.8125,-3.98437 h 1.76563 l 1.26562,1.9375 q 0.35938,0.5625 0.57813,0.9375 0.34375,-0.51563 0.64062,-0.92188 l 1.39063,-1.95312 h 1.6875 l -2.875,3.90625 3.09375,4.39062 h -1.73438 l -1.70312,-2.57812 -0.45313,-0.70313 -2.17187,3.28125 z m 12,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.593
 75,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z"
+       id="path67"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 364.5812,247.31013 V 235.857 h 2.28125 l 2.71875,8.10938 q 0.375,1.125 0.54688,1.6875 0.1875,-0.625 0.60937,-1.82813 l 2.73438,-7.96875 h 2.04687 v 11.45313 h -1.46875 v -9.59375 l -3.32812,9.59375 h -1.35938 l -3.3125,-9.75 v 9.75 z m 18.875,-2.67188 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.83593,4.95313 v -8.29688 h 1.25 v 1.15625 q 0.39063,-0.60937 1.03125,-0.96875 0.65625,-0.375 1.48438,-0.375 0.92187,0 1.51562,0.39063 0.59375,0.375 0
 .82813,1.0625 0.98437,-1.45313 2.5625,-1.45313 1.23437,0 1.89062,0.6875 0.67188,0.67188 0.67188,2.09375 v 5.70313 h -1.39063 v -5.23438 q 0,-0.84375 -0.14062,-1.20312 -0.14063,-0.375 -0.5,-0.59375 -0.35938,-0.23438 -0.84375,-0.23438 -0.875,0 -1.45313,0.57813 -0.57812,0.57812 -0.57812,1.85937 v 4.82813 h -1.40625 v -5.39063 q 0,-0.9375 -0.34375,-1.40625 -0.34375,-0.46875 -1.125,-0.46875 -0.59375,0 -1.09375,0.3125 -0.5,0.3125 -0.73438,0.92188 -0.21875,0.59375 -0.21875,1.71875 v 4.3125 z m 12.79688,-4.15625 q 0,-2.29688 1.28125,-3.40625 1.07812,-0.92188 2.60937,-0.92188 1.71875,0 2.79688,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48438,2.51563 -0.48437,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10938 -1.07812,-1.125 -1.07812,-3.23437 z m 1.45312,0 q 0,1.59375 0.6875,2.39062 0.70313,0.79688 1.75,0.79688 1.04688,0 1.73438,-0.79688 0.70312,-0.79687 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32812 -0.6875,-0.79688 -1.73438,-0.79688 -1.04687,0 -1.75,0.7968
 8 -0.6875,0.78125 -0.6875,2.375 z m 7.96094,4.15625 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 5.28125,3.20312 -0.15625,-1.32812 q 0.45313,0.125 0.79688,0.125 0.46875,0 0.75,-0.15625 0.28125,-0.15625 0.46875,-0.4375 0.125,-0.20313 0.42187,-1.04688 0.0469,-0.10937 0.125,-0.34375 l -3.14062,-8.3125 h 1.51562 l 1.71875,4.79688 q 0.34375,0.92187 0.60938,1.92187 0.23437,-0.96875 0.57812,-1.89062 l 1.76563,-4.82813 h 1.40625 l -3.15625,8.4375 q -0.5,1.375 -0.78125,1.89063 -0.375,0.6875 -0.85938,1.01562 -0.48437,0.32813 -1.15625,0.32813 -0.40625,0 -0.90625,-0.17188 z"
+       id="path69"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path71"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path73"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 330.8464,371.3732 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55469,0 v -8.29688 h 1.26562 v 1.17188 q 0.90625,-1.35938 2.64063,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17187,-1.28125 -0.15625,-0.4375 -0.57813,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64062,0.5625 -0.64062,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.61718,-1.26563 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23437 -0.42188,-0.25 -0.59375,-0.64063 -0.17188,-0.40625 -0.17188,-1.67187 v -4.76563 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60938 0.0625,0.78125 0.0781,0.17188 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z m
  4.07032,4.64063 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path75"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path77"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path79"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 418.20865,381.21695 v -7.20313 h -1.23438 v -1.09375 h 1.23438 v -0.89062 q 0,-0.82813 0.15625,-1.23438 0.20312,-0.54687 0.70312,-0.89062 0.51563,-0.34375 1.4375,-0.34375 0.59375,0 1.3125,0.14062 l -0.20312,1.23438 q -0.4375,-0.0781 -0.82813,-0.0781 -0.64062,0 -0.90625,0.28125 -0.26562,0.26563 -0.26562,1.01563 v 0.76562 h 1.60937 v 1.09375 h -1.60937 v 7.20313 z m 4.11719,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55468,0 v -8.29688 h 1.26563 v 1.17188 q 0.90625,-1.35938 2.64062,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.24219,3.375 q -1.17188,-1.4
 6875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path81"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path83"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path85"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path87"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path89"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path91"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path93"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 532.8373,210.97375 h 99.40155 v 27.46457 H 532.8373 Z"
+       id="path95"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 542.7123,232.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.14062,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 12.14685,-2.21875 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60
 938,1.4375 z m 5.7406,4.125 2.53125,-3.59375 -2.34375,-3.3125 h 1.46875 l 1.0625,1.60937 q 0.29688,0.46875 0.48438,0.78125 0.28125,-0.4375 0.51562,-0.76562 l 1.17188,-1.625 h 1.40625 l -2.39063,3.25 2.5625,3.65625 h -1.4375 l -1.42187,-2.14063 -0.375,-0.59375 -1.8125,2.73438 z m 10.00781,-1.04688 0.17188,1.03125 q -0.5,0.10938 -0.89063,0.10938 -0.64062,0 -1,-0.20313 -0.34375,-0.20312 -0.48437,-0.53125 -0.14063,-0.32812 -0.14063,-1.39062 v -3.96875 h -0.85937 v -0.90625 h 0.85937 v -1.71875 l 1.17188,-0.70313 v 2.42188 h 1.17187 v 0.90625 h -1.17187 v 4.04687 q 0,0.5 0.0469,0.64063 0.0625,0.14062 0.20313,0.23437 0.14062,0.0781 0.40625,0.0781 0.20312,0 0.51562,-0.0469 z m 0.0624,3.70313 v -0.85938 h 7.76563 v 0.85938 z m 8.4906,-2.65625 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.1406
 2,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188
  v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.36561,1.23438 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0.89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z m 6.0531,2.0625 1.15625,-0.1875 q 0.10937,0.70312 0.54687,1.07812 0.45313,0.35938 1.25,0.35938 0.8125,0 1.20
 313,-0.32813 0.39062,-0.32812 0.39062,-0.76562 0,-0.39063 -0.35937,-0.625 -0.23438,-0.15625 -1.1875,-0.39063 -1.29688,-0.32812 -1.79688,-0.5625 -0.48437,-0.25 -0.75,-0.65625 -0.25,-0.42187 -0.25,-0.9375 0,-0.45312 0.20313,-0.84375 0.21875,-0.40625 0.57812,-0.67187 0.28125,-0.1875 0.75,-0.32813 0.46875,-0.14062 1.01563,-0.14062 0.8125,0 1.42187,0.23437 0.60938,0.23438 0.90625,0.64063 0.29688,0.39062 0.40625,1.0625 l -1.14062,0.15625 q -0.0781,-0.53125 -0.45313,-0.82813 -0.375,-0.3125 -1.0625,-0.3125 -0.8125,0 -1.15625,0.26563 -0.34375,0.26562 -0.34375,0.625 0,0.23437 0.14063,0.42187 0.15625,0.1875 0.45312,0.3125 0.17188,0.0625 1.03125,0.29688 1.25,0.32812 1.73438,0.54687 0.5,0.20313 0.78125,0.60938 0.28125,0.40625 0.28125,1 0,0.59375 -0.34375,1.10937 -0.34375,0.51563 -1,0.79688 -0.64063,0.28125 -1.45313,0.28125 -1.34375,0 -2.04687,-0.5625 -0.70313,-0.5625 -0.90625,-1.65625 z m 7.16406,4.71875 v -12.20313 h 2.57812 v 0.96875 h -1.40625 v 10.25 h 1.40625 v 0.98438 z m 5.64044,0
  h -2.59375 v -0.98438 h 1.42188 v -10.25 h -1.42188 v -0.96875 h 2.59375 z"
+       id="path97"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789 z"
+       id="path99"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path101"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path103"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 445.60104,298.39108 h 99.40158 v 27.46457 h -99.40158 z"
+       id="path105"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 455.39792,318.91107 v -6.21875 h 0.9375 v 0.875 q 0.6875,-1.01563 1.98437,-1.01563 0.5625,0 1.03125,0.20313 0.48438,0.20312 0.71875,0.53125 0.23438,0.32812 0.32813,0.76562 0.0469,0.29688 0.0469,1.03125 v 3.82813 h -1.04687 v -3.78125 q 0,-0.65625 -0.125,-0.96875 -0.125,-0.3125 -0.4375,-0.5 -0.3125,-0.20313 -0.73438,-0.20313 -0.67187,0 -1.17187,0.4375 -0.48438,0.42188 -0.48438,1.60938 v 3.40625 z m 7.64258,0 h -0.98438 v -8.59375 h 1.0625 v 3.0625 q 0.67188,-0.82813 1.70313,-0.82813 0.57812,0 1.07812,0.23438 0.51563,0.21875 0.84375,0.64062 0.34375,0.42188 0.53125,1.01563 0.1875,0.59375 0.1875,1.26562 0,1.59375 -0.79687,2.46875 -0.79688,0.875 -1.89063,0.875 -1.10937,0 -1.73437,-0.92187 z m -0.0156,-3.15625 q 0,1.10937 0.3125,1.60937 0.5,0.8125 1.34375,0.8125 0.6875,0 1.1875,-0.59375 0.51563,-0.59375 0.51563,-1.79687 0,-1.21875 -0.48438,-1.79688 -0.48437,-0.57812 -1.17187,-0.57812 -0.6875,0 -1.20313,0.60937 -0.5,0.59375 -0.5,1.73438 z m 4.73633,5.54687 v -0.76562 h 
 7 v 0.76562 z m 11.9082,-4.39062 1.09375,0.125 q -0.25,0.95312 -0.95312,1.48437 -0.70313,0.53125 -1.78125,0.53125 -1.35938,0 -2.17188,-0.84375 -0.79687,-0.84375 -0.79687,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79687,0.84375 0.79687,2.39063 0,0.0937 0,0.28125 h -4.64062 q 0.0625,1.03125 0.57812,1.57812 0.51563,0.53125 1.29688,0.53125 0.57812,0 0.98437,-0.29687 0.42188,-0.3125 0.65625,-0.96875 z m -3.45312,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39063,-1.1875 -0.51562,-0.60937 -1.3125,-0.60937 -0.73437,0 -1.23437,0.48437 -0.48438,0.48438 -0.53125,1.3125 z m 9.9082,3.70313 v -0.78125 q -0.59375,0.92187 -1.73437,0.92187 -0.75,0 -1.375,-0.40625 -0.625,-0.42187 -0.96875,-1.15625 -0.34375,-0.73437 -0.34375,-1.6875 0,-0.92187 0.3125,-1.6875 0.3125,-0.76562 0.9375,-1.15625 0.625,-0.40625 1.39062,-0.40625 0.5625,0 1,0.23438 0.4375,0.23437 0.71875,0.60937 v -3.07812 h 1.04688 v 8.59375 z m -3.32812,-3.10938 q 0,1.20313 0.5,1.79688 0.5,0
 .57812 1.1875,0.57812 0.6875,0 1.17187,-0.5625 0.48438,-0.5625 0.48438,-1.71875 0,-1.28125 -0.5,-1.875 -0.48438,-0.59375 -1.20313,-0.59375 -0.70312,0 -1.17187,0.57813 -0.46875,0.5625 -0.46875,1.79687 z m 5.76758,3.625 1.03125,0.15625 q 0.0625,0.46875 0.35937,0.6875 0.39063,0.29688 1.0625,0.29688 0.73438,0 1.125,-0.29688 0.40625,-0.29687 0.54688,-0.8125 0.0937,-0.32812 0.0781,-1.35937 -0.6875,0.8125 -1.71875,0.8125 -1.28125,0 -1.98437,-0.92188 -0.70313,-0.9375 -0.70313,-2.21875 0,-0.89062 0.3125,-1.64062 0.32813,-0.76563 0.9375,-1.17188 0.60938,-0.40625 1.4375,-0.40625 1.10938,0 1.82813,0.89063 v -0.75 h 0.96875 v 5.375 q 0,1.45312 -0.29688,2.0625 -0.29687,0.60937 -0.9375,0.95312 -0.64062,0.35938 -1.57812,0.35938 -1.10938,0 -1.79688,-0.5 -0.6875,-0.5 -0.67187,-1.51563 z m 0.875,-3.73437 q 0,1.21875 0.48437,1.78125 0.48438,0.5625 1.21875,0.5625 0.73438,0 1.21875,-0.5625 0.5,-0.5625 0.5,-1.75 0,-1.14063 -0.51562,-1.71875 -0.5,-0.57813 -1.21875,-0.57813 -0.70313,0 -1.20313,0.578
 13 -0.48437,0.5625 -0.48437,1.6875 z m 10.25195,1.21875 1.09375,0.125 q -0.25,0.95312 -0.95313,1.48437 -0.70312,0.53125 -1.78125,0.53125 -1.35937,0 -2.17187,-0.84375 -0.79688,-0.84375 -0.79688,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79688,0.84375 0.79688,2.39063 0,0.0937 0,0.28125 h -4.64063 q 0.0625,1.03125 0.57813,1.57812 0.51562,0.53125 1.29687,0.53125 0.57813,0 0.98438,-0.29687 0.42187,-0.3125 0.65625,-0.96875 z m -3.45313,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39062,-1.1875 -0.51563,-0.60937 -1.3125,-0.60937 -0.73438,0 -1.23438,0.48437 -0.48437,0.48438 -0.53125,1.3125 z m 5.45508,1.84375 1.03125,-0.15625 q 0.0937,0.625 0.48438,0.95313 0.40625,0.32812 1.14062,0.32812 0.71875,0 1.0625,-0.28125 0.35938,-0.29687 0.35938,-0.70312 0,-0.35938 -0.3125,-0.5625 -0.21875,-0.14063 -1.07813,-0.35938 -1.15625,-0.29687 -1.60937,-0.5 -0.4375,-0.21875 -0.67188,-0.59375 -0.23437,-0.375 -0.23437,-0.84375 0,-0.40625 0.1875,-0.76562 0.1875,
 -0.35938 0.51562,-0.59375 0.25,-0.17188 0.67188,-0.29688 0.42187,-0.125 0.92187,-0.125 0.71875,0 1.26563,0.21875 0.5625,0.20313 0.82812,0.5625 0.26563,0.35938 0.35938,0.95313 l -1.03125,0.14062 q -0.0625,-0.46875 -0.40625,-0.73437 -0.32813,-0.28125 -0.95313,-0.28125 -0.71875,0 -1.03125,0.25 -0.3125,0.23437 -0.3125,0.5625 0,0.20312 0.125,0.35937 0.14063,0.17188 0.40625,0.28125 0.15625,0.0625 0.9375,0.26563 1.125,0.3125 1.5625,0.5 0.4375,0.1875 0.6875,0.54687 0.25,0.35938 0.25,0.90625 0,0.53125 -0.3125,1 -0.29687,0.45313 -0.875,0.71875 -0.57812,0.25 -1.3125,0.25 -1.21875,0 -1.85937,-0.5 -0.625,-0.51562 -0.79688,-1.5 z"
+       id="path107"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 89.115486,28.062992 H 289.55644 V 55.527557 H 89.115486 Z"
+       id="path109"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 98.084236,54.98299 5.125004,-13.359375 h 1.90625 l 5.46875,13.359375 h -2.01563 l -1.54687,-4.046875 h -5.59375 l -1.468754,4.046875 z m 3.859374,-5.484375 h 4.53125 l -1.40625,-3.703125 q -0.625,-1.6875 -0.9375,-2.765625 -0.26562,1.28125 -0.71875,2.546875 z m 9.84982,5.484375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07813,-1.59375 0.875,0 1.60937,0.3125 0.73438,0.3125 1.09375,0.828125 0.375,0.5 0.51563,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64063 v -5.890625 q 0,-1 -0.20312,-1.484375 -0.1875,-0.5 -0.67188,-0.796875 -0.48437,-0.296875 -1.14062,-0.296875 -1.04688,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 16.68821,-1.1875 q -0.92186,0.765625 -1.76561,1.09375 -0.82813,0.3125 -1.79688,0.3125 -1.59375,0 -2.45312,-0.78125 -0.85938,-0.78125 -0.85938,-1.984375 0,-0.71875 0.32813,-1.296875 0.32812,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45312,-0.25 1.98438,-0.234375 2.92187,-0.5625 0.0156,-
 0.34375 0.0156,-0.421875 0,-1 -0.46874,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70312,0.40625 -0.54688,0.40625 -0.8125,1.421875 l -1.60938,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45313,-0.984375 0.95312,-0.34375 2.1875,-0.34375 1.25,0 2.01561,0.296875 0.78125,0.28125 1.14063,0.734375 0.375,0.4375 0.51562,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10937,2.890625 0.10938,0.59375 0.40625,1.15625 h -1.70312 q -0.26563,-0.515625 -0.32813,-1.1875 z m -0.14062,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01562,0.140625 -1.4375,0.328125 -0.42187,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45313,0.4375 0.9375,0 1.67187,-0.40625 0.75,-0.421875 1.09374,-1.140625 0.26563,-0.5625 0.26563,-1.640625 z m 7.78197,3.390625 0.23437,1.453125 q -0.6875,0.140625 -1.23437,0.140625 -0.89063,0 -1.39063,-0.28125 -0.48437,-0.28125 -0.6875,-0.734375 -0.20312,-0.46875 -0.20312,-1.9375 V 46.57674
  h -1.20313 v -1.265625 h 1.20313 V 42.92049 l 1.625,-0.984375 v 3.375 h 1.65625 v 1.265625 h -1.65625 v 5.671875 q 0,0.6875 0.0781,0.890625 0.0937,0.203125 0.28125,0.328125 0.20313,0.109375 0.57813,0.109375 0.26562,0 0.71875,-0.0625 z m 0.9958,-3.375 q 0,-2.6875 1.48437,-3.96875 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.29759,4.84375 v -9.671875 h 1.46875 v 1.359375 q 0.45313,-0.71875 1.20313,-1.140625 0.76562,-0.4375 1.71875,-0.4375 1.07812,0 1.76562,0.453125 0.6875,0.4375 0.96875,1.234375 1.15625,-1.6875 2.98438,-1
 .6875 1.45312,0 2.21875,0.796875 0.78125,0.796875 0.78125,2.453125 v 6.640625 h -1.64063 v -6.09375 q 0,-0.984375 -0.15625,-1.40625 -0.15625,-0.4375 -0.57812,-0.703125 -0.42188,-0.265625 -0.98438,-0.265625 -1.01562,0 -1.6875,0.6875 -0.67187,0.671875 -0.67187,2.15625 v 5.625 h -1.64063 v -6.28125 q 0,-1.09375 -0.40625,-1.640625 -0.40625,-0.546875 -1.3125,-0.546875 -0.6875,0 -1.28125,0.359375 -0.59375,0.359375 -0.85937,1.0625 -0.25,0.703125 -0.25,2.03125 v 5.015625 z m 15.46268,3.71875 -0.1875,-1.53125 q 0.54687,0.140625 0.9375,0.140625 0.54687,0 0.875,-0.1875 0.32812,-0.171875 0.54687,-0.5 0.15625,-0.25 0.5,-1.21875 0.0469,-0.140625 0.14063,-0.40625 l -3.67188,-9.6875 h 1.76563 l 2.01562,5.59375 q 0.39063,1.078125 0.70313,2.25 0.28125,-1.125 0.67187,-2.203125 l 2.07813,-5.640625 h 1.64062 l -3.6875,9.828125 q -0.59375,1.609375 -0.92187,2.203125 -0.4375,0.8125 -1,1.1875 -0.5625,0.375 -1.34375,0.375 -0.48438,0 -1.0625,-0.203125 z m 13.98018,-8.5625 q 0,-2.6875 1.48437,-3.96875 
 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.68821,4.84375 v -8.40625 h -1.45312 v -1.265625 h 1.45312 v -1.03125 q 0,-0.96875 0.17188,-1.453125 0.23437,-0.640625 0.82812,-1.03125 0.59375,-0.390625 1.67188,-0.390625 0.6875,0 1.53125,0.15625 l -0.25,1.4375 q -0.5,-0.09375 -0.95313,-0.09375 -0.75,0 -1.0625,0.328125 -0.3125,0.3125 -0.3125,1.1875 v 0.890625 h 1.89063 v 1.265625 h -1.89063 v 8.40625 z m 16.28849,-1.1875 q -0.92188,0.765625 -1.76563,1.09375 -0.82812,0.3125 -1.79687,0.3125 -1.59375,0 -2.45313,-0.78125 -0.85937,
 -0.78125 -0.85937,-1.984375 0,-0.71875 0.32812,-1.296875 0.32813,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45313,-0.25 1.98437,-0.234375 2.92187,-0.5625 0.0156,-0.34375 0.0156,-0.421875 0,-1 -0.46875,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70313,0.40625 -0.54687,0.40625 -0.8125,1.421875 l -1.60937,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45312,-0.984375 0.95313,-0.34375 2.1875,-0.34375 1.25,0 2.01563,0.296875 0.78125,0.28125 1.14062,0.734375 0.375,0.4375 0.51563,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10938,2.890625 0.10937,0.59375 0.40625,1.15625 h -1.70313 q -0.26562,-0.515625 -0.32812,-1.1875 z m -0.14063,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01563,0.140625 -1.4375,0.328125 -0.42188,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45312,0.4375 0.9375,0 1.67188,-0.40625 0.75,-0.421875 1.09375,-1.140625 0.26562,-0.5625 0.26562
 ,-1.640625 z m 9.38715,4.859375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07812,-1.59375 0.875,0 1.60938,0.3125 0.73437,0.3125 1.09375,0.828125 0.375,0.5 0.51562,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64062 v -5.890625 q 0,-1 -0.20313,-1.484375 -0.1875,-0.5 -0.67187,-0.796875 -0.48438,-0.296875 -1.14063,-0.296875 -1.04687,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 9.76634,-4.84375 q 0,-2.6875 1.48438,-3.96875 1.25,-1.078125 3.04687,-1.078125 2,0 3.26563,1.3125 1.26562,1.296875 1.26562,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64062,1.65625 -1.0625,0.59375 -2.32813,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79688,2.796875 0.8125,0.921875 2.04687,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23437,0 -2.04687,0.921875 -0.79688,0.90625 -0.79688,2.765625 z m 15.56322,4.84375 v -1.2187
 5 q -0.90625,1.4375 -2.70313,1.4375 -1.15625,0 -2.125,-0.640625 -0.96875,-0.640625 -1.5,-1.78125 -0.53125,-1.140625 -0.53125,-2.625 0,-1.453125 0.48438,-2.625 0.48437,-1.1875 1.4375,-1.8125 0.96875,-0.625 2.17187,-0.625 0.875,0 1.54688,0.375 0.6875,0.359375 1.10937,0.953125 v -4.796875 h 1.64063 V 54.98299 Z m -5.17188,-4.828125 q 0,1.859375 0.78125,2.78125 0.78125,0.921875 1.84375,0.921875 1.07813,0 1.82813,-0.875 0.75,-0.890625 0.75,-2.6875 0,-1.984375 -0.76563,-2.90625 -0.76562,-0.9375 -1.89062,-0.9375 -1.07813,0 -1.8125,0.890625 -0.73438,0.890625 -0.73438,2.8125 z m 15.90697,1.71875 1.6875,0.203125 q -0.40625,1.484375 -1.48438,2.3125 -1.07812,0.8125 -2.76562,0.8125 -2.125,0 -3.375,-1.296875 -1.23438,-1.3125 -1.23438,-3.671875 0,-2.453125 1.25,-3.796875 1.26563,-1.34375 3.26563,-1.34375 1.9375,0 3.15625,1.328125 1.23437,1.3125 1.23437,3.703125 0,0.15625 0,0.4375 h -7.21875 q 0.0937,1.59375 0.90625,2.453125 0.8125,0.84375 2.01563,0.84375 0.90625,0 1.54687,-0.46875 0.64063,
 -0.484375 1.01563,-1.515625 z m -5.39063,-2.65625 h 5.40625 q -0.10937,-1.21875 -0.625,-1.828125 -0.78125,-0.953125 -2.03125,-0.953125 -1.125,0 -1.90625,0.765625 -0.76562,0.75 -0.84375,2.015625 z"
+       id="path111"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path113"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path115"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path117"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path119"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path121"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path123"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path125"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path127"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path129"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path131"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path133"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path135"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path137"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path139"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path141"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path143"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path145"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path147"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,107.32317 h 14.62836 v 5.59806 H 850.9376 Z"
+       id="path149"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path151"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path153"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path155"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,125.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path157"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path159"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path161"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path163"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path165"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path167"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path169"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path171"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path173"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path175"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path177"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path179"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path181"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path183"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path185"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path187"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path189"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path191"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path193"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path195"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path197"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path199"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path201"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path203"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path205"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path207"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path209"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path211"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path213"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,211.32317 h 14.62836 v 5.59807 H 850.9376 Z"
+       id="path215"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path217"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path219"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path221"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,229.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path223"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path225"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path227"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path229"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path231"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path233"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path235"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path237"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path239"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path241"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path243"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path245"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path247"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path249"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path251"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path253"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path255"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path257"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path259"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path261"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path263"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path265"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path267"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path269"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path271"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path273"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path275"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path277"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path279"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,307.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path281"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path283"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path285"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path287"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,325.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path289"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path291"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path293"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path295"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path297"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path299"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path301"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path303"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path305"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path307"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path309"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path311"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path313"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path315"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path317"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path319"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path321"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path323"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path325"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path327"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path329"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path331"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path333"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path335"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path337"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path339"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path341"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path343"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path345"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,403.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path347"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path349"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path351"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path353"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,421.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path355"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path357"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path359"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path361"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path363"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path365"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path367"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path369"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path371"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path373"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path375"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90552,-71.85828 20.47638,-35.92914 40.95276,-71.85827 81.90552,-71.85827"
+       id="path377"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90546,-71.85826 10.23822,-17.96457 20.47638,-35.92915 33.27411,-49.40257 6.39886,-6.73671 13.43762,-12.35065 21.43622,-16.2804 3.99933,-1.96487 8.23858,-3.5087 12.75781,-4.56131 2.25958,-0.52631 4.58911,-0.92981 6.99371,-1.20174 1.20227,-0.13596 2.42334,-0.23902 3.66376,-0.3081 l 0.35388,-0.0172"
+       id="path379"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1456,117.62166 -1.0957,1.15275 3.06024,-1.20262 -3.11731,-1.04582 z"
+       id="path381"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74017,-33.62204 C 722.18577,242.81102 742.87083,226 784.24088,226"
+       id="path383"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74011,-33.62204 10.34253,-8.4055 20.68506,-16.81102 33.61322,-23.11514 6.46405,-3.15207 13.57452,-5.7788 21.6546,-7.61751 4.0401,-0.91934 8.32251,-1.64169 12.88776,-2.1342 2.28265,-0.24626 4.63592,-0.43506 7.06506,-0.5623 1.21448,-0.0636 2.448,-0.11184 3.70105,-0.14415 l 0.39166,-0.009"
+       id="path385"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 780.814,226.03992 -1.11139,1.1376 3.07648,-1.16049 -3.10266,-1.08851 z"
+       id="path387"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 19.86224,3.52755 39.72443,7.05511 79.44885,7.05511"
+       id="path389"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 9.93109,1.76376 19.86218,3.52755 32.27606,4.85037 6.20697,0.66144 13.03455,1.21261 20.79322,1.59842 3.87939,0.19293 7.99151,0.34451 12.37512,0.44784 2.19183,0.0517 4.45147,0.0913 6.78399,0.11798 1.1662,0.0133 2.35065,0.0235 3.55384,0.0303 l 0.2395,9.7e-4"
+       id="path391"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 774.23114,347.34512 -1.12762,1.12155 3.09283,-1.11627 -3.08673,-1.1329 z"
+       id="path393"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90552,22.1575 20.47638,11.07874 40.95276,22.15747 81.90552,22.15747"
+       id="path395"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90546,22.1575 10.23822,5.53937 20.47638,11.07874 33.27411,15.23328 6.39886,2.07724 13.43762,3.80829 21.43622,5.02005 3.99933,0.60583 8.23858,1.08188 12.75781,1.40649 2.25958,0.16226 4.58911,0.28668 6.99371,0.37052 1.20227,0.0419 2.42334,0.0737 3.66376,0.095 l 0.35291,0.005"
+       id="path397"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1446,441.53223 -1.1333,1.11575 3.09845,-1.10037 -3.08087,-1.14874 z"
+       id="path399"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 780.8373,58.973755 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path401"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="M 811.0304,80.77375 V 73.8675 h 1.0625 v 0.984375 q 0.75,-1.140625 2.1875,-1.140625 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.359375 0.375,0.859375 0.0625,0.328125 0.0625,1.140625 v 4.25 h -1.17188 v -4.203125 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.359375 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.796875 v 3.78125 z m 6.97498,-3.453125 q 0,-1.921875 1.07812,-2.84375 0.89063,-0.765625 2.17188,-0.765625 1.42187,0 2.32812,0.9375 0.90625,0.921875 0.90625,2.578125 0,1.328125 -0.40625,2.09375 -0.39062,0.765625 -1.15625,1.1875 -0.76562,0.421875 -1.67187,0.421875 -1.45313,0 -2.35938,-0.921875 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.328125 0.57813,1.984375 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.671875 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.984375 z m 
 11.13123,3.453125 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.453125 -0.6875,-0.453125 -1.07812,-1.265625 -0.375,-0.828125 -0.375,-1.890625 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.453125 1.54687,-0.453125 0.625,0 1.10938,0.265625 0.5,0.25 0.79687,0.671875 v -3.421875 h 1.17188 v 9.546875 z m -3.70313,-3.453125 q 0,1.328125 0.5625,1.984375 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.421875 -0.54687,-2.078125 Q 829.2616,74.68 828.46473,74.68 q -0.78125,0 -1.3125,0.640625 -0.51563,0.625 -0.51563,2 z m 11.3656,1.234375 1.20313,0.140625 q -0.28125,1.0625 -1.0625,1.65625 Q 837.3772,80.93 836.17408,80.93 q -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.609375 0,-1.75 0.89063,-2.703125 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.109375 0,0.3125 h -5.15625 q 0.0625,1.140625 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1
 .10937,-0.328125 0.45313,-0.34375 0.71875,-1.078125 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.859375 -0.4375,-1.296875 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.546875 -0.54688,0.53125 -0.60938,1.4375 z m 9.89667,-0.578125 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.6875,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.296875 0.95313,0.890625 0.375,0.578125 0.59375,1.421875 0.21875,0.828125 0.21875,2.25 0,1.671875 -0.35938,2.703125 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.984375 -0.95313,-1.1875 -0.95313,-3.875 z m 1.20313,0 q 0,2.34375 0.54687,3.125 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.125 0,-2.359375 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path403"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,162.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path405"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,184.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 h -1.17188 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.14062 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path407"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,258.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path409"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,280.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29688 q -0.0156,-0.42188 0.14063,-0.8125 0.23437,-0.64063 0.76562,-1.26563 0.53125,-0.625 1.53125,-1.45312 1.5625,-1.26563 2.10938,-2.01563 0.54687,-0.75 0.54687,-1.40625 0,-0.70312 -0.5,-1.17187 -0.5,-0.48438 -1.29687,-0.48438 -0.85938,0 -1.375,0.51563 -0.5,0.5 -0.5,1.39062 l -1.20313,-0.10937 q 0.125,-1.35938 0.92188,-2.0625 0.8125,-0.70313 2.17187,-0.70313 1.375,0 2.17188,0.76563 0.8125,0.75 0.8125,1.875 0,0.57812 -0.23438,1.14062 -0.23437,0.54688 -0.78125,1.15625 -0.54687,0.60938 -1.8125,1.67188 -1.04687,0.89062 -1.35937,1.21875 -0.29688,0.3125 -0.48438,0.625 z"
+       id="path411"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,354.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path413"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,376.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z"
+       id="path415"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/graph_mem_layout.svg b/doc/guides/prog_guide/img/graph_mem_layout.svg
new file mode 100644
index 000000000..1d41729c9
--- /dev/null
+++ b/doc/guides/prog_guide/img/graph_mem_layout.svg
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg428"
+   sodipodi:docname="Graph_mem_layout.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata434">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs432" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview430"
+     showgrid="false"
+     inkscape:zoom="3.4037037"
+     inkscape:cx="505.84248"
+     inkscape:cy="270.46053"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g426" />
+  <clipPath
+     id="g81c521b992_0_3.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path223" />
+  </clipPath>
+  <g
+     clip-path="url(#g81c521b992_0_3.0)"
+     id="g426">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path226" />
+    <path
+       fill="#ffffff"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path228" />
+    <path
+       stroke="#741b47"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path230" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m143.0105 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path232" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m271.45407 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path234" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 100.52126l129.44095 0"
+       fill-rule="nonzero"
+       id="path236" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 151.71811l129.44095 0"
+       fill-rule="nonzero"
+       id="path238" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 204.91496l129.44095 0"
+       fill-rule="nonzero"
+       id="path240" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 240.11182l129.44095 0"
+       fill-rule="nonzero"
+       id="path242" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 291.30865l129.44095 0"
+       fill-rule="nonzero"
+       id="path244" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 342.50552l129.44095 0"
+       fill-rule="nonzero"
+       id="path246" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 393.70236l129.44095 0"
+       fill-rule="nonzero"
+       id="path248" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 444.8992l129.44095 0"
+       fill-rule="nonzero"
+       id="path250" />
+    <path
+       fill="#000000"
+       d="m172.35416 118.58688l0 -1.125l4.03125 -0.015625l0 3.546875q-0.921875 0.75 -1.921875 1.125q-0.984375 0.359375 -2.03125 0.359375q-1.40625 0 -2.5625 -0.59375q-1.140625 -0.609375 -1.734375 -1.734375q-0.578125 -1.140625 -0.578125 -2.546875q0 -1.40625 0.578125 -2.609375q0.59375 -1.203125 1.6875 -1.78125q1.09375 -0.59375 2.515625 -0.59375q1.03125 0 1.859375 0.34375q0.84375 0.328125 1.3125 0.9375q0.484375 0.59375 0.734375 1.546875l-1.140625 0.3125q-0.21875 -0.71875 -0.53125 -1.140625q-0.3125 -0.421875 -0.90625 -0.671875q-0.59375 -0.25 -1.3125 -0.25q-0.875 0 -1.515625 0.265625q-0.625 0.265625 -1.015625 0.703125q-0.375 0.421875 -0.59375 0.9375q-0.359375 0.875 -0.359375 1.921875q0 1.265625 0.4375 2.125q0.4375 0.859375 1.265625 1.28125q0.84375 0.421875 1.796875 0.421875q0.8125 0 1.59375 -0.3125q0.78125 -0.328125 1.1875 -0.6875l0 -1.765625l-2.796875 0zm5.726425 3.734375l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.593
 75 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q
 -0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.7031
 25 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm11.302963 0l0 -9.546875l1.265625 0l0 3.921875l4.953125 0l0 -3.921875l1.265625 0l0 9.546875l-1.265625 0l0 -4.5l-4.953125 0l0 4.5l-1.265625 0zm14.172028 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.718
 75 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.3
 59375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm7.4749756 3.46875l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234
 375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path252" />
+    <path
+       fill="#000000"
+       d="m161.10791 135.96188l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.631226 -2.03125l1.1875 0.109375q0 0.40625 0.109375 0.609375q0.109375 0.203125 0.34375 0.3125q0.328125 0.140625
  0.828125 0.140625q1.0625 0 1.53125 -0.546875q0.3125 -0.375 0.578125 -1.625l0.109375 -0.5625q-0.921875 0.9375 -1.953125 0.9375q-1.046875 0 -1.75 -0.765625q-0.703125 -0.78125 -0.703125 -2.1875q0 -1.171875 0.546875 -2.140625q0.5625 -0.984375 1.328125 -1.46875q0.765625 -0.5 1.578125 -0.5q1.359375 0 2.09375 1.28125l0.234375 -1.125l1.078125 0l-1.390625 6.671875q-0.21875 1.09375 -0.59375 1.703125q-0.375 0.625 -1.03125 0.953125q-0.65625 0.34375 -1.53125 0.34375q-0.828125 0 -1.4375 -0.21875q-0.59375 -0.203125 -0.890625 -0.625q-0.296875 -0.40625 -0.296875 -0.953125q0 -0.15625 0.03125 -0.34375zm1.46875 -3.6875q0 0.71875 0.140625 1.078125q0.203125 0.5 0.5625 0.765625q0.359375 0.25 0.796875 0.25q0.578125 0 1.140625 -0.40625q0.578125 -0.40625 0.9375 -1.25q0.359375 -0.859375 0.359375 -1.625q0 -0.859375 -0.484375 -1.359375q-0.46875 -0.515625 -1.15625 -0.515625q-0.4375 0 -0.84375 0.234375q-0.390625 0.234375 -0.75 0.703125q-0.34375 0.46875 -0.53125 1.140625q-0.171875 0.65625 -0.171875 0.9843
 75zm6.0062256 3.0625l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.110245 -0.859375q-0.625 0.53125 -1.1875 0.78125q-0.5625 0.234375 -1.203125 0.234375q-0.96875 0 -1.5625 -0.5625q-0.578125 -0.5625 -0.578125 -1.4375q0 -0.578125 0.265625 -1.015625q0.265625 -0.453125 0.703125 -0.71875q0.4375 -0.28125 1.0625 -0.390625q0.40625 -0.078125 1.515625 -0.125q1.109375 -0.046875 1.59375 -0.234375q0.125 -0.484375 0.125 -0.8125q0 -0.40625 -0.296875 -0.640625q-0.40625 -0.328125 -1.1875 -0.328125q-0.75 0 -1.21875 0.328125q-0.46875 0.328125 -0.6875 0.9375l-1.1875 -0.109375q0.359375 -1.015625 1.140625 -1.5625q0.796875 -0.546875 2.0 -0.546875q1.28125 0 2.03125 0.609375q0.578125 0.453125 0.578125 1.1875q0 0.546875 -0.15625 1.28125l-0.390625 1.71875q-0.1875 0.8
 125 -0.1875 1.328125q0 0.328125 0.15625 0.9375l-1.203125 0q-0.09375 -0.34375 -0.125 -0.859375zm0.421875 -2.640625q-0.234375 0.09375 -0.53125 0.15625q-0.28125 0.046875 -0.9375 0.109375q-1.03125 0.078125 -1.453125 0.21875q-0.421875 0.140625 -0.640625 0.453125q-0.21875 0.296875 -0.21875 0.671875q0 0.5 0.34375 0.828125q0.34375 0.3125 0.984375 0.3125q0.578125 0 1.109375 -0.3125q0.546875 -0.3125 0.859375 -0.859375q0.3125 -0.5625 0.484375 -1.578125zm1.7406006 6.15625l2.0 -9.5625l1.09375 0l-0.203125 0.953125q0.59375 -0.625 1.078125 -0.859375q0.484375 -0.25 1.015625 -0.25q0.984375 0 1.625 0.71875q0.65625 0.71875 0.65625 2.0625q0 1.078125 -0.359375 1.96875q-0.34375 0.875 -0.875 1.421875q-0.515625 0.546875 -1.046875 0.796875q-0.53125 0.25 -1.09375 0.25q-1.25 0 -1.921875 -1.265625l-0.78125 3.765625l-1.1875 0zm2.328125 -5.484375q0 0.78125 0.109375 1.078125q0.171875 0.421875 0.53125 0.6875q0.375 0.25 0.875 0.25q1.015625 0 1.640625 -1.140625q0.625 -1.140625 0.625 -2.328125q0 -0.875 -0.4218
 75 -1.359375q-0.421875 -0.484375 -1.046875 -0.484375q-0.453125 0 -0.84375 0.25q-0.375 0.234375 -0.703125 0.703125q-0.328125 0.46875 -0.546875 1.15625q-0.21875 0.6875 -0.21875 1.1875zm5.6624756 2.828125l2.0 -9.546875l1.171875 0l-0.765625 3.671875q0.65625 -0.640625 1.21875 -0.90625q0.578125 -0.28125 1.171875 -0.28125q0.859375 0 1.328125 0.453125q0.484375 0.453125 0.484375 1.1875q0 0.359375 -0.203125 1.34375l-0.859375 4.078125l-1.171875 0l0.875 -4.1875q0.1875 -0.90625 0.1875 -1.140625q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.671875 -0.21875q-0.640625 0 -1.21875 0.34375q-0.578125 0.328125 -0.90625 0.90625q-0.328125 0.578125 -0.609375 1.875l-0.609375 2.96875l-1.1875 0z"
+       fill-rule="nonzero"
+       id="path254" />
+    <path
+       fill="#000000"
+       d="m189.80461 173.51811l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875
  -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.968
 75 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path256" />
+    <path
+       fill="#000000"
+       d="m160.76096 182.86186q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125
  -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.93988 -1.703125q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0625 0.046875 -0.125 0.046875l-2.359375 0l-1.46875 7.296875q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.26
 5625 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 -0.015625 -0.109375l1.46875 -7.296875l-2.359375 0q-0.09375 0 -0.125 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.09375 0.046875 -0.171875q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.078125 0.09375 -0.109375q0.046875 -0.046875 0.109375 -0.046875l5.859375 0q0.078125 0 0.109375 0.0625q0.03125 0.0625 0.03125 0.171875zm5.838608 -0.015625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0468
 75 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm5.4453735 9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625
  -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm9.278656 -9.234375q0 0.046875 -0.015625 0.109375q-0.015625 0.0625 -0.03125 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.078125 0.109375q-0.03125 0.03125 -0.078125 0.03125q-0.09375 0 -0.265625 -0.109375q-0.171875 -0.125 -0.453125 -0.265625q-0.265625 -0.15625 -0.671875 -0.265625q-0.390625 -0.125 -0.9375 -0.125q-0.578125 0 -1.09375 0.171875q-0.5 0.171875 -0.921875 0.484375q-0.421875 0.296875 -0.75 0.703125q-0.328125 0.40625 -0.5625 0.90625q-0.234375 0.484375 -0.359375 1.015625q-0.109375 0.53125 -0.109375 1.078125q0 0.5625 0.15625 1.015625q0.171875 0.4375 0.484375 0.734375q0.3125 0.296875 0.75 0.453125q0.4375 0.15625 0.984375 0.15625q0.40625 0 0.84375 -0.09375q0.453125 -0.09375 0.828125 -0.296875l0.484375 -2.4375l-1.953125 0q-0.078125 0 -0.125 -0.046875q-0.03125 -0.0625 -
 0.03125 -0.171875q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.078125 0.046875 -0.15625q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.0625 0.09375 -0.09375q0.046875 -0.046875 0.109375 -0.046875l2.671875 0q0.1875 0 0.265625 0.125q0.078125 0.125 0.03125 0.328125l-0.640625 3.25q-0.03125 0.125 -0.078125 0.203125q-0.03125 0.0625 -0.09375 0.125q-0.046875 0.0625 -0.296875 0.171875q-0.234375 0.109375 -0.59375 0.234375q-0.359375 0.109375 -0.8125 0.1875q-0.453125 0.09375 -0.953125 0.09375q-0.84375 0 -1.484375 -0.203125q-0.640625 -0.21875 -1.078125 -0.640625q-0.4375 -0.421875 -0.671875 -1.015625q-0.21875 -0.59375 -0.21875 -1.328125q0 -0.703125 0.15625 -1.375q0.15625 -0.671875 0.453125 -1.28125q0.3125 -0.609375 0.75 -1.109375q0.4375 -0.515625 1.0 -0.890625q0.578125 -0.375 1.25 -0.578125q0.6875 -0.21875 1.484375 -0.21875q0.453125 0 0.84375 0.078125q0.40625 0.078125 0.71875 0.203125q0.3125 0.109375 0.515625 0.25q0.21875 0.125 0.296875 0.203125q0.078125 0.0625 0
 .109375 0.140625q0.03125 0.0625 0.03125 0.15625zm6.9862976 0.84375q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0
  -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.424255 6.09375q0.03125 0.140625 0.015625 0.234375q-0.015625 0.078125 -0.078125 0.125q-0.046875 0.046875 -0.1875 0.046875q-0.125 0.015625 -0.34375 0.015625q-0.140625 0 -0.25 -0.015625q-0.109375 0 -0.171875 -0.015625q-0.046875 -0.03125 -0.078125 -0.0625q-0.015625 -0.046875 -0.03125 -0.09375l-0.3125 -2.0625l-3.5 0l-1.09375 2.03125q-0.046875 0.078125 -0.09375 0.
 125q-0.03125 0.03125 -0.109375 0.0625q-0.078125 0.015625 -0.203125 0.015625q-0.109375 0.015625 -0.28125 0.015625q-0.203125 0 -0.3125 -0.015625q-0.125 -0.015625 -0.15625 -0.046875q-0.046875 -0.046875 -0.03125 -0.140625q0.015625 -0.09375 0.09375 -0.234375l4.375 -7.8125q0.046875 -0.078125 0.09375 -0.125q0.0625 -0.046875 0.140625 -0.0625q0.09375 -0.03125 0.21875 -0.03125q0.125 -0.015625 0.3125 -0.015625q0.21875 0 0.34375 0.015625q0.140625 0 0.21875 0.03125q0.09375 0.015625 0.125 0.0625q0.03125 0.046875 0.046875 0.125l1.25 7.828125zm-2.1875 -6.90625l0 0l-2.28125 4.1875l2.921875 0l-0.640625 -4.1875zm9.930588 0.859375q0 0.34375 -0.078125 0.71875q-0.078125 0.375 -0.265625 0.734375q-0.171875 0.359375 -0.4375 0.6875q-0.265625 0.328125 -0.65625 0.578125q-0.375 0.234375 -0.875 0.375q-0.5 0.140625 -1.1875 0.140625l-1.15625 0l-0.59375 3.046875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-
 0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.546875 -7.78125q0.046875 -0.265625 0.203125 -0.375q0.171875 -0.109375 0.390625 -0.109375l1.65625 0q0.328125 0 0.578125 0.03125q0.25 0.015625 0.484375 0.0625q0.359375 0.078125 0.640625 0.25q0.28125 0.15625 0.46875 0.40625q0.203125 0.234375 0.296875 0.546875q0.109375 0.3125 0.109375 0.6875zm-1.1875 0.109375q0 -0.40625 -0.203125 -0.6875q-0.1875 -0.296875 -0.609375 -0.40625q-0.15625 -0.046875 -0.34375 -0.0625q-0.1875 -0.015625 -0.40625 -0.015625l-1.046875 0l-0.671875 3.375l1.0625 0q0.46875 0 0.78125 -0.09375q0.328125 -0.109375 0.5625 -0.28125q0.25 -0.171875 0.40625 -0.390625q0.171875 -0.234375 0.265625 -0.46875q0.109375 -0.25 0.15625 -0.5q0.046875 -0.25 0.046875 -0.46875zm7.76033 6.171875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0
 .015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l0.734375 -3.75l-3.828125 0l-0.734375 3.75q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l1.609375 -8.109375q0.015625 -0.03125 0.046875 -0.0625q0.046875 -0.046875 0.109375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.265625 0.015625q0.109375 0.015625 0.15625 0.046875q0.0625 0.015625 0.078125 0.0625q0.015625 0.03125 0.015625 0.0625l-0.671875 3.390625l3.828125 0l0.671875 -3.390625q0.015625 -0.03125 0.046875 -0.0625q0.03125 -0.046875 0.09375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.28125 -0.015625q0.15625 0 0.25 0.015625q0.109375 0.015625 0.171875 0.046875q0.0625 
 0.015625 0.078125 0.0625q0.015625 0.03125 0 0.0625l-1.609375 8.109375zm7.3821716 1.890625q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625 -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm7.497406 -9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.03125 0.078125 -0.078125 0.15625q-0.03125 0.0625 -0.078125 0.109375q-0.046875 0.046875 -0.125 0.046875l-3.078125 0l-0.5625 2.859375l2.90625 0q0.078125 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.140625q0 0.0625 -0.015625 0.140625q0 0.0625 -0.015625 0.140625q-
 0.015625 0.0625 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.90625 0l-0.703125 3.5q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0.015625q-0.109375 -0.015625 -0.171875 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.109375l1.5625 -7.796875q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l3.78125 0q0.09375 0 0.125 0.0625q0.03125 0.0625 0.03125 0.15625zm6.3270416 0q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.07812
 5 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm6.7109985 7.734375q-0.015625 0.140625 -0.078125 0.234375q-0.0625 0.078125 -0.140625 0.140625q-0.0625 0.0625 -0.15625 0.09375q-0.078125 0.015625 -0.171875 0.015625l-0.421875 0q-0.171875 0 -0.296875 -0.03125q-0.109375 -0.046875 -0.203125 -0.140625q-0.078125 -0.09375 -0.15625 -0.234375q-0.0625 -0
 .15625 -0.140625 -0.390625l-1.578125 -4.484375q-0.15625 -0.484375 -0.328125 -0.953125q-0.15625 -0.484375 -0.296875 -0.96875l-0.015625 0q-0.078125 0.53125 -0.1875 1.0625q-0.09375 0.515625 -0.203125 1.046875l-0.96875 4.90625q-0.015625 0.0625 -0.0625 0.109375q-0.03125 0.03125 -0.09375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.140625 0 -0.25 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.0625 -0.046875q-0.015625 -0.046875 0 -0.109375l1.546875 -7.765625q0.046875 -0.265625 0.21875 -0.375q0.171875 -0.109375 0.34375 -0.109375l0.5 0q0.15625 0 0.265625 0.03125q0.125 0.03125 0.203125 0.125q0.09375 0.078125 0.15625 0.21875q0.078125 0.125 0.15625 0.328125l1.59375 4.5625q0.140625 0.421875 0.28125 0.84375q0.15625 0.421875 0.296875 0.84375l0.015625 0q0.09375 -0.53125 0.203125 -1.09375q0.109375 -0.578125 0.203125 -1.109375l0.921875 -4.5625q0.015625 -0.0625 0.046875 -0.09375q0.03125 -0.03125 0.09375 -0.0625q0.0625 -0.03125 0.15
 625 -0.03125q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.25 0.015625q0.109375 0 0.15625 0.03125q0.0625 0.03125 0.078125 0.0625q0.015625 0.03125 0.015625 0.09375l-1.5625 7.765625zm9.090393 -7.09375q0 0.140625 -0.046875 0.34375q-0.046875 0.203125 -0.125 0.328125q-0.078125 0.109375 -0.15625 0.109375q-0.09375 0 -0.21875 -0.125q-0.125 -0.125 -0.328125 -0.265625q-0.203125 -0.140625 -0.53125 -0.25q-0.328125 -0.125 -0.828125 -0.125q-0.546875 0 -1.0 0.203125q-0.4375 0.203125 -0.8125 0.5625q-0.359375 0.34375 -0.640625 0.796875q-0.265625 0.453125 -0.453125 0.953125q-0.171875 0.5 -0.265625 1.015625q-0.078125 0.5 -0.078125 0.953125q0 0.515625 0.125 0.921875q0.125 0.40625 0.375 0.6875q0.25 0.28125 0.609375 0.421875q0.359375 0.140625 0.8125 0.140625q0.515625 0 0.875 -0.109375q0.375 -0.109375 0.625 -0.25q0.265625 -0.140625 0.4375 -0.25q0.1875 -0.125 0.296875 -0.125q0.078125 0 0.109375 0.0625q0.03125 0.046875 0.03125 0.15625q0 0.03125 -0.015625 0.09375q-0.015625 0.0625 -0.03125 0.14062
 5q0 0.0625 -0.015625 0.15625q-0.015625 0.078125 -0.046875 0.15625q-0.03125 0.0625 -0.0625 0.125q-0.015625 0.0625 -0.09375 0.125q-0.0625 0.0625 -0.28125 0.203125q-0.21875 0.125 -0.53125 0.234375q-0.3125 0.109375 -0.71875 0.1875q-0.390625 0.09375 -0.828125 0.09375q-0.671875 0 -1.203125 -0.203125q-0.53125 -0.203125 -0.90625 -0.578125q-0.375 -0.390625 -0.578125 -0.96875q-0.203125 -0.578125 -0.203125 -1.328125q0 -0.609375 0.125 -1.265625q0.140625 -0.65625 0.390625 -1.265625q0.25 -0.625 0.625 -1.171875q0.390625 -0.546875 0.890625 -0.953125q0.5 -0.421875 1.125 -0.65625q0.640625 -0.25 1.390625 -0.25q0.484375 0 0.890625 0.109375q0.421875 0.109375 0.703125 0.28125q0.296875 0.15625 0.421875 0.28125q0.140625 0.125 0.140625 0.296875zm6.2602844 -0.640625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.07
 8125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.60935974 3.0l3.3437347 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.0468597 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.4999847 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875z"
+       fill-rule="nonzero"
+       id="path258" />
+    <path
+       fill="#000000"
+       d="m172.59329 223.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859
 375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.250732 0l0 -9.546875l3.59375 0q1.09375 0 1.75 0.296875q0.65625 0.28125 1.03125 0.890625q0.375 0.609375 0.375 1.265625q0 0.609375 -0.34375 1.15625q-0.328125 0.53125 -0.984375 0.859375q0.859375 0.25 1.328125 0.875q0.46875 0.609375 0.
 46875 1.4375q0 0.671875 -0.296875 1.25q-0.28125 0.578125 -0.703125 0.890625q-0.40625 0.3125 -1.03125 0.46875q-0.625 0.15625 -1.546875 0.15625l-3.640625 0zm1.265625 -5.53125l2.0625 0q0.84375 0 1.203125 -0.109375q0.484375 -0.140625 0.71875 -0.46875q0.25 -0.34375 0.25 -0.84375q0 -0.46875 -0.234375 -0.828125q-0.21875 -0.359375 -0.640625 -0.5q-0.421875 -0.140625 -1.453125 -0.140625l-1.90625 0l0 2.890625zm0 4.40625l2.375 0q0.609375 0 0.859375 -0.046875q0.4375 -0.078125 0.734375 -0.25q0.296875 -0.1875 0.484375 -0.53125q0.1875 -0.359375 0.1875 -0.8125q0 -0.53125 -0.28125 -0.921875q-0.265625 -0.40625 -0.75 -0.5625q-0.484375 -0.15625 -1.40625 -0.15625l-2.203125 0l0 3.28125zm12.06163 1.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 
 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm3.1624756 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm8.156113 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625
  0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path260" />
+    <path
+       fill="#000000"
+       d="m186.7589 261.9118l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896713 -0.5781
 25q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875z"
+       fill-rule="nonzero"
+       id="path262" />
+    <path
+       fill="#000000"
+       d="m163.32709 275.55243l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path264" />
+    <path
+       fill="#000000"
+       d="m186.7589 313.10867l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 
 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318588 4.125
 l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125z"
+       fill-rule="nonzero"
+       id="path266" />
+    <path
+       fill="#000000"
+       d="m163.32709 326.7493l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.42187
 5 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125 
 0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -
 1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125
 q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125 
 1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.0156
 25 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-
 0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path268" />
+    <path
+       fill="#000000"
+       d="m186.7589 364.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052963 3.0l0 
 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0z"
+       fill-rule="nonzero"
+       id="path270" />
+    <path
+       fill="#000000"
+       d="m163.32709 377.94614l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path272" />
+    <path
+       fill="#000000"
+       d="m185.65256 415.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625
  0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365463 4.12
 5l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0z"
+       fill-rule="nonzero"
+       id="path274" />
+    <path
+       fill="#000000"
+       d="m163.32709 429.14297l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path276" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m92.06693 62.29134l230.33072 0l0 27.464565l-230.33072 0z"
+       fill-rule="evenodd"
+       id="path278" />
+    <path
+       fill="#000000"
+       d="m114.3782 84.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0164948 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.8748627 -1.171875l1.2031174 0.140625q-0.28125 1.0625 -1.0624924 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375
  -0.96875q1.390625 0 2.265625 0.9375q0.8749924 0.9375 0.8749924 2.65625q0 0.109375 0 0.3125l-5.1562424 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.443718 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.271851 -2.078125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125 2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.
 390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm6.6312256 3.578125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -
 0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.45
 3125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm10.677963 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -
 0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.17984 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.43
 75zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875zm4.8434753 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836807 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0
 .9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 
 1.453125l0 3.578125l-1.171875 0zm10.664932 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071198 2.65625l-0.125 -1.09375q0.375 0.109375 0.65
 625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625
 q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875
  0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1
 .234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path280"
+       style="fill:#c8ab37" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925z"
+       fill-rule="evenodd"
+       id="path282" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path284" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path286" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m64.91338 302.8189l99.40157 0l0 27.46457l-99.40157 0z"
+       fill-rule="evenodd"
+       id="path288" />
+    <path
+       fill="#000000"
+       d="m74.71026 323.3389l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.
 609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm7.658203 -2.390625l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.283203 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625
  -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm10.017578 3.109375l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm10.220703 1.109375l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2
 .390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -
 0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path290" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m687.0105 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path292" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m815.45404 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path294" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 100.52126l129.44092 0"
+       fill-rule="nonzero"
+       id="path296" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 135.71811l129.44092 0"
+       fill-rule="nonzero"
+       id="path298" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 172.91496l129.44092 0"
+       fill-rule="nonzero"
+       id="path300" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 208.11182l129.44092 0"
+       fill-rule="nonzero"
+       id="path302" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 243.30865l129.44092 0"
+       fill-rule="nonzero"
+       id="path304" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 278.50552l129.44092 0"
+       fill-rule="nonzero"
+       id="path306" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 313.70236l129.44092 0"
+       fill-rule="nonzero"
+       id="path308" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 348.8992l129.44092 0"
+       fill-rule="nonzero"
+       id="path310" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 384.09607l129.44092 0"
+       fill-rule="nonzero"
+       id="path312" />
+    <path
+       fill="#000000"
+       d="m733.8046 122.32126l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 
 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.9687
 5 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path314" />
+    <path
+       fill="#000000"
+       d="m709.2222 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm9.155334 3.0625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm2.5392456 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.9281006 3.453125l-2.125 -6.90625l1.21875 0l1.09375 3.984375l0.421875 1.484375q0.015625 -0.109375 0.359375 -1.421875l1.09375 -4.046875l1.203125 0l1.03125 4.0l0.34375 1.328125l0.40
 625 -1.34375l1.171875 -3.984375l1.140625 0l-2.15625 6.90625l-1.21875 0l-1.09375 -4.140625l-0.265625 -1.171875l-1.40625 5.3125l-1.21875 0zm8.343872 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.60
 9375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.031
 25q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6311035 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.6
 09375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0
 625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q
 -1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.
 53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path316" />
+    <path
+       fill="#000000"
+       d="m710.1765 191.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.234497 -0.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375
  -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6468506 3.453125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm9.974976 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.
 171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874878 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-
 0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.
 296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.2031
 25q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.87
 5 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625z"
+       fill-rule="nonzero"
+       id="path318" />
+    <path
+       fill="#000000"
+       d="m711.206 229.9118l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.438232 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-
 0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.5218506 1.40625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.
 265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 3.703125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125
  0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875
 q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0
 .140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6310425 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.187
 5q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.188416 -2.21875l1.203125 0.140625q-0.281
 25 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125
  0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path320" />
+    <path
+       fill="#000000"
+       d="m706.683 265.10867l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.14
 0625l-0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765
 625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1
 .90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.8967285 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.56251526 1.71875 -0.56251526q0.78125 0 1.359375 0.3125q0.578125 0.29689026 0.953125 0.89064026q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.
 21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm10.2404785 7.359375l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.
 671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path322" />
+    <path
+       fill="#000000"
+       d="m706.683 300.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.375
  -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42
 1875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.3186035 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm7.0217285 2.65625l0 -9.5625l1.07812
 5 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.0
 78125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path324" />
+    <path
+       fill="#000000"
+       d="m706.683 335.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.37
 5 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 
 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4
 21875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052979 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375
  -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm5.2873535 3.78125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.7
 5 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path326" />
+    <path
+       fill="#000000"
+       d="m705.57666 370.69922l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218872 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.
 375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843506 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.42187
 5 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1
 .421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm13.1875 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875
  0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.062
 5 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path328" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m636.06696 70.291336l230.33069 0l0 27.46457l-230.33069 0z"
+       fill-rule="evenodd"
+       id="path330" />
+    <path
+       fill="#000000"
+       d="m660.59735 92.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0165405 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874817 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.
 96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.4437256 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.490601 -2.65625l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.9
 0625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.07
 8125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.7873535 0.671875q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0
 .921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359
 375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.179871 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q
 0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.26
 5625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.07812
 5zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.5
 78125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4
 375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.4923096 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0
 .375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 
 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.7
 03125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path332"
+       style="fill:#008033" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013z"
+       fill-rule="evenodd"
+       id="path334" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path336" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path338" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m821.979 278.50656l99.40155 0l0 27.46457l-99.40155 0z"
+       fill-rule="evenodd"
+       id="path340" />
+    <path
+       fill="#000000"
+       d="m831.7759 299.02655l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0
 .609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm11.908203 -4.390625l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.908203 3.703125l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q
 0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm5.767578 3.625l1.03125 0.15625q0.0625 0.46875 0.359375 0.6875q0.390625 0.296875 1.0625 0.296875q0.734375 0 1.125 -0.296875q0.40625 -0.296875 0.546875 -0.8125q0.09375 -0.328125 0.078125 -1.359375q-0.6875 0.8125 -1.71875 0.8125q-1.28125 0 -1.984375 -0.921875q-0.703125 -0.9375 -0.703125 -2.21875q0 -0.890625 0.3125 -1.640625q0.328125 -0.765625 0.9375 -1.171875q0.609375 -0.40625 1.4375 -0.40625q1.109375 0 1.828125 0.890625l0 -0.75l0.96875 0l0 5.375q0 1.453125 -0.296875 2.0625q-0.296875 0.609375 -0.9375 0.953125q-0.640625 0.359375 -1.578125 0.359375q-1.109375 0 -1.796875 -0.5q-0.6875 -0.5 -0.671875 -1.515625zm0.875 -3.734375q0 1.21875 0.484375 1.7
 8125q0.484375 0.5625 1.21875 0.5625q0.734375 0 1.21875 -0.5625q0.5 -0.5625 0.5 -1.75q0 -1.140625 -0.515625 -1.71875q-0.5 -0.578125 -1.21875 -0.578125q-0.703125 0 -1.203125 0.578125q-0.484375 0.5625 -0.484375 1.6875zm10.251953 1.21875l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.35937
 5 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path342" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m359.4252 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path344" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m520.34906 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path346" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 100.52126l161.92126 0"
+       fill-rule="nonzero"
+       id="path348" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 135.71811l161.92126 0"
+       fill-rule="nonzero"
+       id="path350" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 170.91496l161.92126 0"
+       fill-rule="nonzero"
+       id="path352" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 206.11182l161.92126 0"
+       fill-rule="nonzero"
+       id="path354" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 241.30865l161.92126 0"
+       fill-rule="nonzero"
+       id="path356" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 276.50552l161.92126 0"
+       fill-rule="nonzero"
+       id="path358" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 311.70236l161.92126 0"
+       fill-rule="nonzero"
+       id="path360" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 346.8992l161.92126 0"
+       fill-rule="nonzero"
+       id="path362" />
+    <path
+       fill="#000000"
+       d="m379.84702 119.25876l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.1937256 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359
 375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.802948 1.25q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.4531
 25l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-
 0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.906
 25 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path364" />
+    <path
+       fill="#000000"
+       d="m379.84702 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.615601 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm6.584198 -3.453125q0 -1.921875 1.07812
 5 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0
 .125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.3437
 5 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.17
 1875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path366" />
+    <path
+       fill="#000000"
+       d="m376.8892 189.65247l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921
 875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.
 546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.
 328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42187
 5 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.7500305 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921
 875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0
 .484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03
 125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203
 125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path368" />
+    <path
+       fill="#000000"
+       d="m372.88116 227.9118l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896698 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.8029785 1.2
 5q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0
 .734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.
 34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm
 9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path370" />
+    <path
+       fill="#000000"
+       d="m372.88116 263.10867l0 -9.54689l6.90625 0l0 1.125l-5.640625 0l0 2.9218903l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5000153l0 7.5000153l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.5
 78125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.87
 5 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318573 4.125l-1.171875 0l0 -7.4687653q-0.421875 0.40626526 -1.109375 0.81251526q-0.6875 0.40625 -1.234375 0.609375l0 -1.1406403q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.57814zm6.5842285 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.6562
 5q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.
 359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.
 15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.7187653l1.171875 -0.703125l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path372" />
+    <path
+       fill="#000000"
+       d="m372.88116 298.3055l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052948 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm4.8498535 -2.328
 125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 
 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-
 0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375
 zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path374" />
+    <path
+       fill="#000000"
+       d="m371.7748 333.50235l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.7178955 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.75 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65
 625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625
  -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0
 .9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path376" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.77692 65.05774l280.09448 0l0 27.46457l-280.09448 0z"
+       fill-rule="evenodd"
+       id="path378" />
+    <path
+       fill="#000000"
+       d="m345.39603 83.51399l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.8593
 75 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.969635 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 0l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578
 125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836792 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0
 .8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.6649475 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.42187
 5q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-
 2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875
  0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.9218
 75 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.1093
 75q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.1247253 1.046875l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9842224 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.4531
 25 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 
 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm11.084351 1.203125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.521881 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.187
 5 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.896851 0l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm6.6468506 -4.734
 375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.9454346 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm7.1937256 0.578125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125
  2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm9.8811035 1.515625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.
 078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.
 34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375
  -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875
  -1.171875l0 -0.421875zm2.9906006 3.46875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.633667 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.2
 5 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625z"
+       fill-rule="nonzero"
+       id="path380"
+       style="fill:#88aa00" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m516.2336 178.6483l115.4646 0l0 27.464554l-115.4646 0z"
+       fill-rule="evenodd"
+       id="path382" />
+    <path
+       fill="#000000"
+       d="m532.2961 196.15266l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm5.9960938 -1.859375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.062
 5 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5zm8.71875 0.921875l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm5.0996094 0.171875q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.
 265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.35937
 5 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125z"
+       fill-rule="nonzero"
+       id="path384" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m589.60895 206.11285l-61.259888 0"
+       fill-rule="evenodd"
+       id="path386" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m589.60895 206.11285l-55.259888 0"
+       fill-rule="evenodd"
+       id="path388"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m534.34906 204.46114l-4.538086 1.6517181l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path390" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m520.7349 322.6483l115.46454 0l0 27.46457l-115.46454 0z"
+       fill-rule="evenodd"
+       id="path392" />
+    <path
+       fill="#000000"
+       d="m536.7974 340.15268l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm10.667969 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.876953 3.703125l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0
 .765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.705078 0l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875z"
+       fill-rule="nonzero"
+       id="path394" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m586.3438 346.90027l-61.259827 0"
+       fill-rule="evenodd"
+       id="path396" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m586.3438 346.90027l-55.259827 0"
+       fill-rule="evenodd"
+       id="path398"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m531.084 345.24854l-4.538086 1.6517334l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path400" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.45407 204.69554l85.51181 -102.645676"
+       fill-rule="evenodd"
+       id="path402" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.45407 204.69554l81.67139 -98.03577"
+       fill-rule="evenodd"
+       id="path404" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m354.39453 107.716995l1.6356201 -4.5439224l-4.1737366 2.4294815z"
+       fill-rule="evenodd"
+       id="path406" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 240.03412l88.0 105.60629"
+       fill-rule="evenodd"
+       id="path408" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 240.03412l84.15903 100.99686"
+       fill-rule="evenodd"
+       id="path410" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m353.94522 342.08835l4.1740417 2.4289856l-1.6362 -4.5437317z"
+       fill-rule="evenodd"
+       id="path412" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 392.81104l411.87402 -290.77167"
+       fill-rule="evenodd"
+       id="path414" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 392.81104l406.9724 -287.31128"
+       fill-rule="evenodd"
+       id="path416" />
+    <path
+       fill="#274e13"
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m678.98016 106.84912l2.7546997 -3.9666214l-4.659912 1.2679062z"
+       fill-rule="evenodd"
+       id="path418" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 442.09448l415.9685 -61.102356"
+       fill-rule="evenodd"
+       id="path420" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 442.09448l410.03223 -60.230347"
+       fill-rule="evenodd"
+       id="path422" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m681.3274 383.49832l4.249878 -2.2937317l-4.7299805 -0.9746704z"
+       fill-rule="evenodd"
+       id="path424" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/link_the_nodes.svg b/doc/guides/prog_guide/img/link_the_nodes.svg
new file mode 100644
index 000000000..4a127e67c
--- /dev/null
+++ b/doc/guides/prog_guide/img/link_the_nodes.svg
@@ -0,0 +1,3330 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg1003"
+   sodipodi:docname="Link_the_nodes.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata1009">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs1007" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview1005"
+     showgrid="false"
+     inkscape:zoom="1.2361274"
+     inkscape:cx="1097.0658"
+     inkscape:cy="171.4074"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1003" />
+  <clipPath
+     id="g7c2ed6c54d_0_300.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path2" />
+  </clipPath>
+  <g
+     clip-path="url(#g7c2ed6c54d_0_300.0)"
+     id="g1001">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path5" />
+    <path
+       fill="#ffffff"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path7" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path9" />
+    <path
+       fill="#ffffff"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path11" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path13" />
+    <path
+       fill="#fdf8f8"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path15" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path17" />
+    <path
+       fill="#d9ead3"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path19" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path21" />
+    <path
+       fill="#cfe2f3"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path23" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path25" />
+    <path
+       fill="#f4cccc"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path27" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path29" />
+    <path
+       fill="#f4cccc"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path31" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path33" />
+    <path
+       fill="#eeeeee"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path35" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path37" />
+    <path
+       fill="#eeeeee"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path39" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path41" />
+    <path
+       fill="#eeeeee"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path43" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path45" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m199.20312 266.6246l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path47" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482z"
+       fill-rule="evenodd"
+       id="path49" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path51" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path53" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m191.56721 277.2064l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path55" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path57" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path59" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path61" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path63" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path65" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path67" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path69" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path71" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path73" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path75" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m145.16678 229.68527l73.13281 0l0 16.320053l-73.13281 0z"
+       fill-rule="evenodd"
+       id="path77" />
+    <path
+       fill="#000000"
+       d="m164.66463 247.24533l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827484 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.54685974 -0.375 -0.85935974 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.82810974 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.9531097 -2.75q0 1.046875 0.43748474 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.42185974 0.5 -0.42185974 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.5
 15625 1.40625q0.46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.78125q0.25 -0.40625 0.6875 -0.65625q0.4375 -0.25 0.984375 -0.25q0.609375 0 1.0 0.265625q0.390625 0.25 0.5625 0.703125q0.65625 -0.96875 1.703125 -0.96875q0.828125 0 1.265625 0.46875q0.4375 0.453125 0.4375 1.390625l0 3.796875l-0.921875 0l0 -3.484375q0 -0.5625 -0.09375 -0.796875q-0.09375 -0.25 -0.34375 -0.40625q-0.234375 -0.15625 -0.546875 -0.15625q-0.59375 0 -0.984375 0.390625q-0.375 0.390625 -0.375 1.25l0 3.203125l-0.9375 0l0 -3.59375q0 -0.625 -0.234375 -0.9375q-0.21875 -0.3125 -0.75 -0.3125q-0.390625 0 -0.734375 0.21875q-0.328125 0.203125 -0.484375 0.609375q-0.140625 0.390625 -0.140625 1.15625l0 2.859375l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path79" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path81" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path83" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path85" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path87" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path89" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path91" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path93" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path95" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path97" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path99" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path101" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path103" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path105" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path107" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path109" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path111" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path113" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path115" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path117" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path119" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 186.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path121" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path123" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path125" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path127" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 197.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path129" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path131" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path133" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path135" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path137" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path139" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path141" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path143" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path145" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path147" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path149" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 149.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path151" />
+    <path
+       fill="#000000"
+       d="m286.14026 167.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path153" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path155" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path157" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path159" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path161" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path163" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path165" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path167" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path169" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path171" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path173" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path175" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path177" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path179" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path181" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path183" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path185" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path187" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path189" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path191" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path193" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 346.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path195" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path197" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path199" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path201" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 357.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path203" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path205" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path207" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path209" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path211" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path213" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path215" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path217" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path219" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path221" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path223" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 309.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path225" />
+    <path
+       fill="#000000"
+       d="m286.14026 327.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.840271 0.53125q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.46875 1.578125z"
+       fill-rule="nonzero"
+       id="path227" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path229" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path231" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path233" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path235" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path237" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path239" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path241" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path243" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path245" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path247" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path249" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path251" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path253" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path255" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path257" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path259" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path261" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path263" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path265" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path267" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 442.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path269" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path271" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path273" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path275" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 453.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path277" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path279" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path281" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path283" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path285" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path287" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path289" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path291" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path293" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path295" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path297" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 405.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path299" />
+    <path
+       fill="#000000"
+       d="m406.43945 423.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.809021 1.640625l0.921875 -0.140625q0.078125 0.5625 0.4375 0.859375q0.359375 0.296875 1.0 0.296875q0.640625 0 0.953125 -0.265625q0.3125 -0.265625 0.3125 -0.625q0 -0.3125 -0.28125 -0.5q-0.1875 -0.125 -0.953125 -0.3125q-1.03125 -0.265625 -1.4375 -0.453125q-0.390625 -0.1875 -0.59375 -0.515625q-0.203125 -0.34375 -0.203125 -0.75q0 -0.359375 0.171875 -0.671875q0.171875 -0.328125 0.453125 -0.53125q0.21875 -0.15625 0.59375 -0.265625q0.390625 -0.125 0.8125 -0.125q0.65625 0 1.140625 0.1875q0.5 0.1875 0.734375 0.515625q0.234375 0.3125 0.3125 0.859375l-0.90625 0.125q-0.0625 -0.4375 -0.375 -0.671875q-0.296875 -0.234375 -0.828125 -0.234375q-0.65625 0 -0.9375 0.21875q-0.265625 0.203125 -0.265625 0.484375q0 0.1875 0.109375 0.328125
 q0.125 0.15625 0.359375 0.25q0.140625 0.0625 0.828125 0.25q1.0 0.265625 1.390625 0.4375q0.390625 0.15625 0.609375 0.484375q0.234375 0.3125 0.234375 0.796875q0 0.46875 -0.28125 0.890625q-0.265625 0.40625 -0.78125 0.640625q-0.515625 0.21875 -1.171875 0.21875q-1.078125 0 -1.640625 -0.4375q-0.5625 -0.453125 -0.71875 -1.34375z"
+       fill-rule="nonzero"
+       id="path301" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path303" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path305" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path307" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path309" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path311" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path313" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path315" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path317" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path319" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path321" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path323" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path325" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path327" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path329" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path331" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path333" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path335" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path337" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path339" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path341" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 314.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path343" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path345" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path347" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path349" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 325.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path351" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path353" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path355" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path357" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path359" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path361" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path363" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path365" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path367" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path369" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path371" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 277.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path373" />
+    <path
+       fill="#000000"
+       d="m407.32922 295.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.582733 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.168396 3.296875l0 -5.53125l0.84375 0l0 0.84375q0.328125 -0.59375 0.59375 -0.78125q0.28125 -0.1875 0.609375 -0.1875q0.46875 0 0.953125 0.3125l-0.3125 0.859375q-0.34375 -0.203125 -0.6875 -0.203125q-0.3125 0 -0.5625 0.1875q-0.234375 0.1875 -0.34375 0.515625q-0.15625 0.5 -0.15625 1.09375l0 2.890625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path375" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path377" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path379" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path381" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path383" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path385" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path387" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path389" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path391" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path393" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path395" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path397" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path399" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path401" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path403" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path405" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path407" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path409" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path411" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path413" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path415" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 210.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path417" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path419" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path421" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path423" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 221.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path425" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path427" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path429" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path431" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path433" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path435" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path437" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path439" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path441" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path443" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path445" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 173.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path447" />
+    <path
+       fill="#000000"
+       d="m414.14026 191.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.699646 5.421875l0 -2.71875q-0.21875 0.3125 -0.609375 0.515625q-0.390625 0.203125 -0.828125 0.203125q-0.984375 0 -1.703125 -0.78125q-0.703125 -0.796875 -0.703125 -2.15625q0 -0.828125 0.28125 -1.484375q0.296875 -0.671875 0.84375 -1.015625q0.546875 -0.34375 1.203125 -0.34375q1.03125 0 1.609375 0.875l0 -0.75l0.84375 0l0 7.65625l-0.9375 0zm-2.875 -4.90625q0 1.0625 0.4375 1.609375q0.453125 0.53125 1.078125 0.53125q0.59375 0 1.015625 -0.5q0.4375 -0.515625 0.4375 -1.546875q0 -1.109375 -0.453125 -1.65625q-0.453125 -0.5625 -1.0625 -0.5625q-0.609375 0 -1.03125 0.515625q-0.421875 0.515625 -0.421875 1.609375z"
+       fill-rule="nonzero"
+       id="path449" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path451" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path453" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path455" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path457" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path459" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path461" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path463" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path465" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path467" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path469" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path471" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path473" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path475" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path477" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path479" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path481" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path483" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path485" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path487" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path489" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 106.62759l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path491" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path493" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path495" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path497" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path499" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path501" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path503" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path505" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path507" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path509" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path511" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path513" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path515" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path517" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path519" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path521" />
+    <path
+       fill="#000000"
+       d="m414.14026 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 5.421875l0 -7.65625l0.859375 0l0 0.71875q0.296875 -0.421875 0.671875 -0.625q0.390625 -0.21875 0.921875 -0.21875q0.703125 0 1.25 0.375q0.546875 0.359375 0.8125 1.03125q0.28125 0.65625 0.28125 1.453125q0 0.84375 -0.3125 1.53125q-0.296875 0.671875 -0.875 1.03125q-0.578125 0.359375 -1.21875 0.359375q-0.46875 0 -0.84375 -0.1875q-0.375 -0.203125 -0.609375 -0.515625l0 2.703125l-0.9375 0zm0.84375 -4.859375q0 1.0625 0.4375 1.578125q0.4375 0.515625 1.046875 0.515625q0.625 0 1.0625 -0.53125q0.453125 -0.53125 0.453125 -1.640625q0 -1.046875 -0.4375 -1.578125q-0.4375 -0.53125 -1.046875 -0.53125q-0.59375 0 -1.0625 0.5625q-0.453125 0.5625 -0.453125 1.625z"
+       fill-rule="nonzero"
+       id="path523" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path525" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path527" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path529" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path531" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path533" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path535" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path537" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path539" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path541" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path543" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path545" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path547" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path549" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path551" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path553" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path555" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path557" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path559" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path561" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path563" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path565" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path567" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path569" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path571" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path573" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path575" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path577" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path579" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path581" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path583" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path585" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path587" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path589" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path591" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path593" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path595" />
+    <path
+       fill="#000000"
+       d="m566.14026 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.809021 3.296875l0 -0.8125q-0.65625 0.9375 -1.75 0.9375q-0.5 0 -0.921875 -0.1875q-0.421875 -0.1875 -0.625 -0.46875q-0.203125 -0.28125 -0.296875 -0.703125q-0.046875 -0.265625 -0.046875 -0.875l0 -3.421875l0.9375 0l0 3.0625q0 0.734375 0.046875 1.0q0.09375 0.359375 0.375 0.578125q0.296875 0.203125 0.703125 0.203125q0.421875 0 0.796875 -0.203125q0.375 -0.21875 0.515625 -0.59375q0.15625 -0.375 0.15625 -1.078125l0 -2.96875l0.9375 0l0 5.53125l-0.828125 0z"
+       fill-rule="nonzero"
+       id="path597" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path599" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path601" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path603" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path605" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path607" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path609" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path611" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path613" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path615" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path617" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path619" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path621" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path623" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path625" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path627" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path629" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path631" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path633" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path635" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path637" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 298.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path639" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path641" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path643" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path645" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 309.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path647" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path649" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path651" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path653" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path655" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path657" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path659" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path661" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path663" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path665" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path667" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 261.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path669" />
+    <path
+       fill="#000000"
+       d="m566.43945 279.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm9.715271 3.296875l-2.09375 -5.53125l0.984375 0l1.1875 3.3125q0.1875 0.53125 0.359375 1.109375q0.109375 -0.4375 0.34375 -1.046875l1.21875 -3.375l0.96875 0l-2.09375 5.53125l-0.875 0z"
+       fill-rule="nonzero"
+       id="path671" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path673" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path675" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path677" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path679" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path681" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path683" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path685" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path687" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path689" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path691" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path693" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path695" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path697" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path699" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path701" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path703" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path705" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path707" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path709" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path711" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 434.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path713" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path715" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path717" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path719" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 445.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path721" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path723" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path725" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path727" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path729" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path731" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path733" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path735" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path737" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path739" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path741" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 397.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path743" />
+    <path
+       fill="#000000"
+       d="m566.43945 415.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.559021 3.296875l2.015625 -2.875l-1.859375 -2.65625l1.171875 0l0.84375 1.296875q0.234375 0.375 0.390625 0.625q0.21875 -0.34375 0.40625 -0.609375l0.9375 -1.3125l1.125 0l-1.921875 2.609375l2.0625 2.921875l-1.15625 0l-1.125 -1.71875l-0.296875 -0.46875l-1.453125 2.1875l-1.140625 0z"
+       fill-rule="nonzero"
+       id="path745" />
+    <path
+       fill="#ffffff"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path747" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path749" />
+    <path
+       fill="#ffffff"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path751" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path753" />
+    <path
+       fill="#fdf8f8"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path755" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path757" />
+    <path
+       fill="#d9ead3"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path759" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path761" />
+    <path
+       fill="#cfe2f3"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path763" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path765" />
+    <path
+       fill="#f4cccc"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path767" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path769" />
+    <path
+       fill="#f4cccc"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path771" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path773" />
+    <path
+       fill="#eeeeee"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path775" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path777" />
+    <path
+       fill="#eeeeee"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path779" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path781" />
+    <path
+       fill="#eeeeee"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path783" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path785" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.16473 106.62759l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path787" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path789" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path791" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path793" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m583.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path795" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path797" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path799" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path801" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path803" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path805" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path807" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path809" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path811" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path813" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path815" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m537.1668 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path817" />
+    <path
+       fill="#000000"
+       d="m559.62317 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm10.230896 2.453125l0.125 0.828125q-0.390625 0.09375 -0.703125 0.09375q-0.5 0 -0.78125 -0.15625q-0.28125 -0.171875 -0.40625 -0.4375q-0.109375 -0.265625 -0.109375 -1.109375l0 -3.171875l-0.6875 0l0 -0.734375l0.6875 0l0 -1.359375l0.9375 -0.5625l0 1.921875l0.9375 0l0 0.734375l-0.9375 0l0 3.234375q0 0.390625 0.046875 0.515625q0.046875 0.109375 0.15625 0.1875q0.109375 0.0625 0.328125 0.0625q0.15625 0 0.40625 -0.046875z"
+       fill-rule="nonzero"
+       id="path819" />
+    <path
+       fill="#ffffff"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path821" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path823" />
+    <path
+       fill="#ffffff"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path825" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path827" />
+    <path
+       fill="#fdf8f8"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path829" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path831" />
+    <path
+       fill="#d9ead3"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path833" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path835" />
+    <path
+       fill="#cfe2f3"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path837" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path839" />
+    <path
+       fill="#f4cccc"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path841" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path843" />
+    <path
+       fill="#f4cccc"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path845" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path847" />
+    <path
+       fill="#eeeeee"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path849" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path851" />
+    <path
+       fill="#eeeeee"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path853" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path855" />
+    <path
+       fill="#eeeeee"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path857" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path859" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m783.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path861" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path863" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path865" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path867" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m775.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path869" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path871" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path873" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path875" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path877" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path879" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path881" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path883" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path885" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path887" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path889" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m729.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path891" />
+    <path
+       fill="#000000"
+       d="m750.43945 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.137146 5.421875l-0.09375 -0.875q0.296875 0.078125 0.53125 0.078125q0.3125 0 0.5 -0.109375q0.1875 -0.09375 0.3125 -0.28125q0.078125 -0.140625 0.28125 -0.703125q0.03125 -0.078125 0.078125 -0.21875l-2.09375 -5.546875l1.015625 0l1.140625 3.203125q0.234375 0.609375 0.40625 1.28125q0.15625 -0.640625 0.375 -1.265625l1.1875 -3.21875l0.9375 0l-2.109375 5.625q-0.328125 0.90625 -0.515625 1.25q-0.25 0.46875 -0.578125 0.6875q-0.3125 0.21875 -0.765625 0.21875q-0.265625 0 -0.609375 -0.125z"
+       fill-rule="nonzero"
+       id="path893" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m211.71075 275.29062c16.11023 0 24.165344 -13.937012 32.220474 -27.874023c8.055115 -13.937012 16.110214 -27.874008 32.220474 -27.874008"
+       fill-rule="evenodd"
+       id="path895" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m211.71075 275.29062c16.11023 0 24.16536 -13.937012 32.220474 -27.874008c4.0275574 -6.968506 8.055115 -13.937012 13.089554 -19.163391c2.5172424 -2.6131897 5.286194 -4.790848 8.432709 -6.315201c1.5732727 -0.7621918 3.2409363 -1.3610382 5.018738 -1.7693481c0.4444275 -0.10209656 0.89575195 -0.19224548 1.3542175 -0.27009583c0.22921753 -0.038909912 0.4602356 -0.07473755 0.6930847 -0.107437134l0.2121582 -0.028259277"
+       fill-rule="evenodd"
+       id="path897" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m272.7317 219.76288l-1.0499573 1.1945496l3.011078 -1.3208618l-3.1556702 -0.923645z"
+       fill-rule="evenodd"
+       id="path899" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m206.79628 284.1058c0 19.13388 14.29921 28.700806 28.598434 38.26773c14.29921 9.566925 28.59842 19.13385 28.59842 38.2677"
+       fill-rule="evenodd"
+       id="path901" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m206.79628 284.1058c0 19.13385 14.29921 28.700775 28.59842 38.26773c7.1496124 4.7834473 14.299225 9.566925 19.661423 15.546234c2.6810913 2.989685 4.915344 6.2783203 6.4793396 10.015381c0.7819824 1.8685608 1.3963928 3.8492126 1.8153076 5.9606323c0.20947266 1.0557556 0.37008667 2.144165 0.47827148 3.2676392l0.00491333 0.05441284"
+       fill-rule="evenodd"
+       id="path903" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m263.83395 357.21786l-1.1755981 -1.0711365l1.2668762 3.0341797l0.9798584 -3.1386719z"
+       fill-rule="evenodd"
+       id="path905" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m324.40216 194.87454c0 -41.086624 33.811005 -82.17323 67.62204 -82.17323"
+       fill-rule="evenodd"
+       id="path907" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m324.40216 194.87454c0 -20.543304 8.452759 -41.086624 21.131866 -56.494095c6.339569 -7.7037506 13.735748 -14.123528 21.660187 -18.617378c3.9622498 -2.2469177 8.056519 -4.0123596 12.216888 -5.216072c2.0801697 -0.6018524 4.17688 -1.0632782 6.281769 -1.3742294c0.5262451 -0.07774353 1.0529785 -0.14608002 1.5801392 -0.20485687c0.26358032 -0.029388428 0.5272217 -0.056381226 0.7909546 -0.080963135l0.53601074 -0.045051575"
+       fill-rule="evenodd"
+       id="path909" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.6 112.841896l-1.0775146 1.1697693l3.0410461 -1.2503891l-3.1333008 -0.9968872z"
+       fill-rule="evenodd"
+       id="path911" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.75735 207.661c36.834625 0 73.66928 40.34645 73.66928 80.69292"
+       fill-rule="evenodd"
+       id="path913" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.75735 207.66098c18.417297 0 36.834625 10.086624 50.647614 25.216537c6.906494 7.564972 12.661926 16.390747 16.690704 25.84694c2.0144043 4.728119 3.597168 9.613831 4.6762695 14.578339c0.5395813 2.4822388 0.9532471 4.984192 1.2320251 7.4959717c0.1394043 1.2559204 0.24505615 2.5142822 0.31588745 3.7738953l0.017486572 0.35531616"
+       fill-rule="evenodd"
+       id="path915" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m400.33734 284.92798l-1.1535034 -1.0949097l1.2047119 3.0594177l1.0437012 -3.1180115z"
+       fill-rule="evenodd"
+       id="path917" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m331.69748 203.27638c15.078735 0 22.618134 3.3543396 30.157501 6.708664c7.5393677 3.3543396 15.078735 6.708664 30.15747 6.708664"
+       fill-rule="evenodd"
+       id="path919" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m331.69748 203.2764c15.078766 0 22.618134 3.3543243 30.157501 6.7086487c3.7696838 1.6771698 7.5393677 3.3543396 12.251495 4.612213c2.3560486 0.62893677 4.947693 1.1530457 7.8927917 1.519928c1.4725037 0.18344116 3.0333862 0.32757568 4.6973267 0.42582703c0.41601562 0.024597168 0.83847046 0.046279907 1.2675476 0.06503296l0.62176514 0.024765015"
+       fill-rule="evenodd"
+       id="path921" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.5859 216.63281l-1.1443787 1.1044312l3.109253 -1.0695038l-3.069275 -1.179306z"
+       fill-rule="evenodd"
+       id="path923" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m323.7046 355.2753c36.17325 0 72.346466 -3.1653748 72.346466 -6.330719"
+       fill-rule="evenodd"
+       id="path925" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m323.7046 355.2753c18.08664 0 36.17325 -0.7913208 49.73819 -1.9783325c6.782501 -0.59350586 12.43457 -1.2859497 16.390991 -2.0278015c0.98913574 -0.18551636 1.8722534 -0.37402344 2.6405945 -0.5649414l0.5545349 -0.14355469"
+       fill-rule="evenodd"
+       id="path927" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m393.02893 350.56067l-0.46139526 1.5220032l2.1943665 -2.4487l-3.2549744 0.4653015z"
+       fill-rule="evenodd"
+       id="path929" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.78412 359.65433c0 44.519684 28.614166 89.0394 57.228333 89.0394"
+       fill-rule="evenodd"
+       id="path931" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.7841 359.65433c0 22.259827 7.1535645 44.519684 17.88388 61.21457c5.3651733 8.347443 11.624512 15.30365 18.330963 20.172974c3.3532104 2.4346619 6.818207 4.3476562 10.339111 5.6519165c1.760437 0.6521301 3.534851 1.1521301 5.316223 1.4890747c0.44540405 0.084228516 0.8911743 0.15826416 1.3372803 0.22195435l0.60028076 0.0786438"
+       fill-rule="evenodd"
+       id="path933" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m380.59183 448.4835l-1.1914368 1.0534973l3.1529236 -0.9329529l-3.0149841 -1.3119812z"
+       fill-rule="evenodd"
+       id="path935" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.3904 229.5441c22.905518 0 34.358246 -5.2125854 45.811005 -10.425186c11.452759 -5.2126007 22.905518 -10.425201 45.811035 -10.425201"
+       fill-rule="evenodd"
+       id="path937" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.3904 229.54411c22.905518 0 34.358276 -5.2126007 45.811035 -10.425201c5.7263794 -2.606308 11.452759 -5.2126007 18.610748 -7.167328c3.5789795 -0.97735596 7.515869 -1.7918243 11.989563 -2.3619537c2.2368774 -0.2850647 4.607971 -0.50904846 7.13562 -0.6617737c1.263794 -0.07633972 2.5668335 -0.13487244 3.9116821 -0.17433167l0.73657227 -0.018814087"
+       fill-rule="evenodd"
+       id="path939" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58563 208.7347l-1.111084 1.1379547l3.0761108 -1.1614532l-3.1029663 -1.0875549z"
+       fill-rule="evenodd"
+       id="path941" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905518 -39.425186 41.811005 -52.566925c20.905548 -13.141724 41.811066 -26.283463 41.811066 -52.566925"
+       fill-rule="evenodd"
+       id="path943" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905487 -39.4252 41.811005 -52.566925c10.452759 -6.570862 20.905548 -13.141739 28.745087 -21.355316c3.9197998 -4.1067963 7.1862793 -8.624268 9.472839 -13.757751c1.1432495 -2.566742 2.041504 -5.287491 2.6539917 -8.187912c0.30621338 -1.4502106 0.5410156 -2.945343 0.69921875 -4.4886017c0.03955078 -0.38581085 0.07434082 -0.7746353 0.10424805 -1.1665115l0.013000488 -0.18595123"
+       fill-rule="evenodd"
+       id="path945" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m535.90155 124.26837l1.0835571 1.1641159l-1.0132446 -3.1280365l-1.234436 3.0475311z"
+       fill-rule="evenodd"
+       id="path947" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m454.75735 231.661c22.314941 0 33.472443 20.960617 44.629913 41.92125c11.157471 20.960632 22.314941 41.921265 44.629944 41.921265"
+       fill-rule="evenodd"
+       id="path949" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m454.75735 231.66098c22.314941 0 33.472412 20.960632 44.629913 41.921265c5.5787354 10.480316 11.157471 20.960632 18.13092 28.820862c3.4866943 3.9301147 7.3220825 7.2052307 11.680481 9.497803c2.1791992 1.1462708 4.4891357 2.046936 6.95166 2.6610107c1.2312012 0.30703735 2.5006104 0.54241943 3.810852 0.7010803c0.16375732 0.019836426 0.32818604 0.03845215 0.49328613 0.055877686l0.13989258 0.013671875"
+       fill-rule="evenodd"
+       id="path951" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.59436 315.33255l-1.1792603 1.0671082l3.1420288 -0.9690857l-3.0298462 -1.2772827z"
+       fill-rule="evenodd"
+       id="path953" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.78412 455.65433c24.307068 0 36.4606 -3.7401428 48.614166 -7.480316c12.153534 -3.7401428 24.307098 -7.4802856 48.614166 -7.4802856"
+       fill-rule="evenodd"
+       id="path955" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m446.78412 455.65433c24.307098 0 36.460632 -3.7401733 48.614166 -7.480316c6.076782 -1.8700867 12.153564 -3.7401733 19.749542 -5.1427307c3.7979736 -0.7012634 7.975708 -1.285675 12.723206 -1.6947632c2.3737793 -0.20455933 4.8898926 -0.36523438 7.5722656 -0.47479248c1.3411255 -0.05480957 2.723816 -0.09680176 4.151062 -0.12512207l0.99108887 -0.017120361"
+       fill-rule="evenodd"
+       id="path957" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58545 440.71948l-1.1160889 1.1329956l3.0812378 -1.1477966l-3.0981445 -1.1012878z"
+       fill-rule="evenodd"
+       id="path959" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-68.0 10.0 -136.0 52.5 -136.0 104.99999"
+       fill-rule="evenodd"
+       id="path961" />
+    <path
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-34.0 5.0 -68.0 18.125 -93.5 36.562492c-12.75 9.21875 -23.375 19.765625 -30.8125 31.289062c-3.71875 5.7617188 -6.640625 11.767586 -8.6328125 17.973633c-0.99606323 3.1030273 -1.7597351 6.2561035 -2.274414 9.453735c-0.25732422 1.5988159 -0.45239258 3.2087708 -0.5831299 4.829178c-0.032684326 0.4051056 -0.061340332 0.81085205 -0.0859375 1.2172546l-0.013824463 0.24894714"
+       fill-rule="evenodd"
+       id="path963" />
+    <path
+       fill="#980000"
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m301.8311 146.25957l-1.0921936 -1.1560669l1.0363464 3.1204681l1.2119141 -3.0565796z"
+       fill-rule="evenodd"
+       id="path965" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m628.3904 213.5441c24.905518 0 37.358276 -1.2125854 49.811035 -2.4251862c12.452759 -1.2126007 24.905518 -2.4252014 49.811035 -2.4252014"
+       fill-rule="evenodd"
+       id="path967" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m628.39044 213.54411c24.905518 0 37.358215 -1.2126007 49.810974 -2.4252014c6.2263794 -0.606308 12.452759 -1.2126007 20.235779 -1.6673279c3.8914795 -0.22735596 8.172058 -0.41682434 13.036499 -0.54945374c2.432129 -0.0663147 5.010254 -0.11842346 7.758606 -0.15393066c1.3741455 -0.01777649 2.7908936 -0.031402588 4.253235 -0.04055786l1.0998535 -0.0051574707"
+       fill-rule="evenodd"
+       id="path969" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m724.5854 208.7025l-1.1217041 1.1274567l3.086914 -1.1324921l-3.0926514 -1.1166687z"
+       fill-rule="evenodd"
+       id="path971" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m622.7841 431.67007c0 -45.75589 36.58264 -68.63385 73.165344 -91.51181c36.582703 -22.87793 73.165344 -45.75589 73.165344 -91.511795"
+       fill-rule="evenodd"
+       id="path973" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m622.7841 431.67007c0 -45.75592 36.582703 -68.63385 73.165344 -91.51178c18.29132 -11.438995 36.582703 -22.87796 50.30121 -37.176697c6.859253 -7.149353 12.575256 -15.013641 16.576538 -23.950348c2.0005493 -4.4683533 3.5724487 -9.204803 4.644226 -14.254028c0.5359497 -2.5246277 0.94677734 -5.1274414 1.2236328 -7.814026c0.13842773 -1.3433075 0.24334717 -2.70755 0.3137207 -4.093445l0.035339355 -0.7969208"
+       fill-rule="evenodd"
+       id="path975" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m769.04407 252.07283l1.1011353 1.1475525l-1.0605469 -3.11232l-1.1881104 3.0658875z"
+       fill-rule="evenodd"
+       id="path977" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.7573 117.00678c36.88977 0 55.334656 13.614174 73.77954 27.22834c18.444885 13.6141815 36.88977 27.228348 73.77954 27.228348"
+       fill-rule="evenodd"
+       id="path979" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m598.7573 117.006775c36.88977 0 55.334656 13.6141815 73.77954 27.228348c9.222473 6.807083 18.444946 13.6141815 29.972961 18.719482c5.764038 2.552658 12.104431 4.679886 19.30951 6.16893c3.602478 0.7445221 7.4211426 1.3295135 11.492004 1.728363c2.0354614 0.19943237 4.13385 0.35231018 6.2998657 0.45535278c0.5415039 0.025772095 1.0872803 0.04840088 1.6373291 0.06788635c0.2750244 0.009719849 0.5510864 0.018676758 0.8282471 0.026824951l0.8128052 0.021453857"
+       fill-rule="evenodd"
+       id="path981" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m742.8896 171.42342l-1.1376343 1.1113739l3.1026611 -1.0884094l-3.076416 -1.160614z"
+       fill-rule="evenodd"
+       id="path983" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m451.69748 115.27639c21.078735 0 31.618134 -0.645668 42.1575 -1.2913437c10.539368 -0.645668 21.078735 -1.291336 42.15747 -1.291336"
+       fill-rule="evenodd"
+       id="path985" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m451.6975 115.27639c21.078735 0 31.618103 -0.645668 42.15747 -1.291336c5.269684 -0.32283783 10.539368 -0.645668 17.126495 -0.8877945c3.2935486 -0.12106323 6.9164734 -0.22195435 11.033356 -0.29257202c2.0584717 -0.035308838 4.2404785 -0.063056946 6.5665894 -0.081970215c1.1630249 -0.009460449 2.3620605 -0.016708374 3.5997314 -0.021591187l0.40423584 -7.9345703E-4"
+       fill-rule="evenodd"
+       id="path987" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m532.5854 112.70034l-1.1224365 1.1267548l3.0876465 -1.1305542l-3.09198 -1.1186066z"
+       fill-rule="evenodd"
+       id="path989" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m604.3975 309.543c67.82678 0 135.6535 -36.299225 135.6535 -72.59842"
+       fill-rule="evenodd"
+       id="path991" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m604.3975 309.543c33.91339 0 67.82678 -9.074799 93.26178 -22.687012c12.717529 -6.8060913 23.31543 -14.7465515 30.734009 -23.25418c3.7092896 -4.2538147 6.623657 -8.649414 8.610779 -13.115921c0.9935913 -2.233261 1.7553711 -4.4842224 2.2686157 -6.7440643c0.12835693 -0.56495667 0.2411499 -1.1304779 0.3381958 -1.6963959c0.04852295 -0.28297424 0.09307861 -0.5660553 0.13366699 -0.84921265c0.020263672 -0.14157104 0.039611816 -0.28315735 0.057922363 -0.42478943l0.049560547 -0.4055481"
+       fill-rule="evenodd"
+       id="path993" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m739.852 240.36588l1.0574341 1.1879883l-0.94329834 -3.1498566l-1.302124 3.0192566z"
+       fill-rule="evenodd"
+       id="path995" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m49.937008 5.086614l504.22046 0l0 27.464567l-504.22046 0z"
+       fill-rule="evenodd"
+       id="path997" />
+    <path
+       fill="#000000"
+       d="m60.296383 32.00661l0 -13.359373l1.78125 0l0 11.78125l6.562496 0l0 1.5781231l-8.343746 0zm10.250713 -11.468748l0 -1.890625l1.640625 0l0 1.890625l-1.640625 0zm0 11.468748l0 -9.671873l1.640625 0l0 9.671873l-1.640625 0zm4.144821 0l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm10.375717 0l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm18.089554 -1.4687481l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -
 1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051788 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.078125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000717 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.437
 5l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324654 5.765623l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm9.766342 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.296
 8731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm15.563217 4.843748l0 -1.2187481q-0.90625 1.4374981 -2.703125 1.4374981q-1.15625 0 -2.125 -0.6406231q-0.96875 -0.640625 -1.5 -1.78125q-0.53125 -1.140625 -0.53125 -2.625q0 -1.453125 0.484375 -2.625q0.484375 -1.1875 1.4375 -1.8125q0.96875 -0.625 2.171875 -0.625q0.875 0 1.546875 0.375q0.6875 0.359375 1.109375 0.953125l0 -4.796875l1.640625 0l0 13.359373l-1.53125 0zm-5.171875 -4.828123q0 1.859375 0.78125 2.78125q0.78125 0.921875 1.84375 0.921875q1.078125 0 1.828125 -0.875q0.75 -0.890625 0.75 -2.6875q0 -1.984375 -0.765625 -2.90625q-0.765625 -0.9375 -1.890625 -0.9375q-1.078125 0 -1.8125 0.890625q-0.734375 0.890625 -0.734375 2.8125zm15.906967 1.71875l1.6875 0.203125q-0.40625 1.484375 -1.
 484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm8.485092 2.875l1.625 -0.25q0.125 0.96875 0.75 1.5q0.625 0.515625 1.75 0.515625q1.125 0 1.671875 -0.453125q0.546875 -0.46875 0.546875 -1.09375q0 -0.546875 -0.484375 -0.875q-0.328125 -0.21875 -1.671875 -0.546875q-1.8125 -0.46875 -2.515625 -0.796875q-0.6875 -0.328125 -1.046875 -0.90625q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.640625 0.296875 -1.1875q0.296875 -0.5625 0.8125 -0.921875q0.375 -0.28125 1.03125 -0.46875q0.671
 875 -0.203125 1.421875 -0.203125q1.140625 0 2.0 0.328125q0.859375 0.328125 1.265625 0.890625q0.421875 0.5625 0.578125 1.5l-1.609375 0.21875q-0.109375 -0.75 -0.640625 -1.171875q-0.515625 -0.421875 -1.46875 -0.421875q-1.140625 0 -1.625 0.375q-0.46875 0.375 -0.46875 0.875q0 0.3125 0.1875 0.578125q0.203125 0.265625 0.640625 0.4375q0.234375 0.09375 1.4375 0.421875q1.75 0.453125 2.4375 0.75q0.6875 0.296875 1.078125 0.859375q0.390625 0.5625 0.390625 1.40625q0 0.828125 -0.484375 1.546875q-0.46875 0.71875 -1.375 1.125q-0.90625 0.3906231 -2.046875 0.3906231q-1.875 0 -2.875 -0.7812481q-0.984375 -0.78125 -1.25 -2.328125zm18.745804 1.421875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.2
 03125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm0.99580383 -3.375q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm20.793396 1.296875l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0
 .96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.0 3.546873l0 -9.671873l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.062498l-1.625 0zm12.853302 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.
 484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm15.453842 4.578125q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.421875q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.4
 0625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm7.781967 3.390625l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm8.230179 -1.640625l1.6874847 0.203125q-0.40625 1.484375 -1.4843597 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.6
 71875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.1562347 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.2187347 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm17.902756 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051941 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.07
 8125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000702 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324646 9.468748l0 -13.374998l1.484375 0l0 1
 .25q0.53125 -0.734375 1.1875 -1.09375q0.671875 -0.375 1.625 -0.375q1.234375 0 2.171875 0.640625q0.953125 0.625 1.4375 1.796875q0.484375 1.15625 0.484375 2.546875q0 1.484375 -0.53125 2.671875q-0.53125 1.1875 -1.546875 1.828125q-1.015625 0.6249981 -2.140625 0.6249981q-0.8125 0 -1.46875 -0.3437481q-0.65625 -0.34375 -1.0625 -0.875l0 4.703123l-1.640625 0zm1.484375 -8.484373q0 1.859375 0.75 2.765625q0.765625 0.890625 1.828125 0.890625q1.09375 0 1.875 -0.921875q0.78125 -0.9375 0.78125 -2.875q0 -1.84375 -0.765625 -2.765625q-0.75 -0.921875 -1.8125 -0.921875q-1.046875 0 -1.859375 0.984375q-0.796875 0.96875 -0.796875 2.84375zm15.203857 3.59375q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.42187
 5q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.40625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm10.516357 1.3125l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3
 .71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.015625 3.546873l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm15.953125 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.01562
 5 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm12.719482 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.179077 1.4687481l0 -8.406248l-1.453125 0l0 -1.265625l1.453125 0l0 -1.03125q0 -0.96875 0.171875 -1.453125q0.234375 -0.640625 0.828125 -1.03125q0.59375 -0.390625 1.671875 -0.390625q0.6875 0 1.53125 0.15625l-0.25 1.4375q-0.5 -0.09375 -0.953125 -0.09375q-0.75 0 -1.0625 0.328125q-0.3125 0.3125 -0.3125 1.1875l0 0.8906
 25l1.890625 0l0 1.265625l-1.890625 0l0 8.406248l-1.625 0zm4.7457886 0l0 -13.359373l1.640625 0l0 13.359373l-1.640625 0zm3.5823364 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm11.078857 4.843748l-2.96875 -9.671873l1.703125 0l1.53125 5.578125l0.578125 2.078125q0.046875 -0.15625 0.5 -2.0l1.546875 -5.65625l1.6875 0l1.4375 5.609375l0.484375 1.84375l0.5625 -1.859375l1.65625 -5.59375l1.59375 0l-3.03125 9.671873l-1.703125 0l-1.53125 -5.796873l-0.375 -1.640625l-1.953125 7.437498l-1.71
 875 0z"
+       fill-rule="nonzero"
+       id="path999" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index fb250abf5..a98b16f0e 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -57,6 +57,7 @@ Programmer's Guide
     metrics_lib
     bpf_lib
     ipsec_lib
+    graph_lib
     source_org
     dev_kit_build_system
     dev_kit_root_make_help
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index 000bbf501..d208547ec 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -62,6 +62,30 @@ New Features
 
   * Added support for matching on IPv4 Time To Live and IPv6 Hop Limit.
 
+* **Added rte_graph library.**
+
+  Graph architecture abstracts the data processing functions as a ``node`` and
+  ``links`` them together to create a complex ``graph`` to enable reusable/modular
+  data processing functions. The graph library provides API to enable graph
+  framework operations such as create, lookup, dump and destroy on graph and node
+  operations such as clone, edge update, and edge shrink, etc.
+  The API also allows to create the stats cluster to monitor per graph and per node stats.
+
+* **Added rte_node library which consists of a set of packet processing nodes.**
+
+  The rte_node library that consists of nodes used by rte_graph library. Each
+  node performs a specific packet processing function based on application
+  configuration. The following nodes are added:
+
+  * Null node: Skeleton node that defines the general structure of a node.
+  * Ethernet device node: Consists of ethernet Rx/Tx nodes as well as ethernet
+    control APIs.
+  * IPv4 lookup node: Consists of ipv4 extract and lpm lookup node. Routes can
+    be configured by the application through ``rte_node_ip4_route_add`` function.
+  * IPv4 rewrite node: Consists of ipv4 and ethernet header rewrite functionality
+    that can be configured through ``rte_node_ip4_rewrite_add`` function.
+  * Packet drop node: Frees the packets received to their respective mempool.
+
 
 Removed Items
 -------------
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v4 29/29] doc: add l3fwd graph application user guide
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (27 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 28/29] doc: add graph library programmer's guide guide jerinj
@ 2020-04-05  8:56       ` jerinj
  2020-04-09 23:13       ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem Andrzej Ostruszka
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-05  8:56 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic, Ori Kam,
	Bruce Richardson, Radu Nicolau, Akhil Goyal, Tomasz Kantecki,
	Sunil Kumar Kori, Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark, xiao.w.wang

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Adding the user guide for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                                   |   1 +
 doc/guides/rel_notes/release_20_05.rst        |   8 +
 doc/guides/sample_app_ug/index.rst            |   1 +
 doc/guides/sample_app_ug/intro.rst            |   4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst | 327 ++++++++++++++++++
 5 files changed, 341 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index de57f452b..870cd8cae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1576,6 +1576,7 @@ F: doc/guides/sample_app_ug/l3_forward.rst
 
 M: Nithin Dabilpuram <ndabilpuram@marvell.com>
 F: examples/l3fwd-graph/
+F: doc/guides/sample_app_ug/l3_forward_graph.rst
 
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index d208547ec..9d3e7bd69 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -86,6 +86,14 @@ New Features
     that can be configured through ``rte_node_ip4_rewrite_add`` function.
   * Packet drop node: Frees the packets received to their respective mempool.
 
+* **Added new l3fwd-graph sample application.**
+
+  Added an example application ``l3fwd-graph``. It demonstrates the usage of graph
+  library and node library for packet processing. In addition to the library usage
+  demonstration, this application can use for performance comparison with existing
+  ``l3fwd`` (The static code without any nodes) with the modular ``l3fwd-graph``
+  approach.
+
 
 Removed Items
 -------------
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index ac3445147..cf9c1e44d 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -29,6 +29,7 @@ Sample Applications User Guides
     l2_forward_event
     l2_forward_cat
     l3_forward
+    l3_forward_graph
     l3_forward_power_man
     l3_forward_access_ctrl
     link_status_intr
diff --git a/doc/guides/sample_app_ug/intro.rst b/doc/guides/sample_app_ug/intro.rst
index 6cd0342a1..8ff223b16 100644
--- a/doc/guides/sample_app_ug/intro.rst
+++ b/doc/guides/sample_app_ug/intro.rst
@@ -54,6 +54,10 @@ examples are highlighted below.
   forwarding, or ``l3fwd`` application does forwarding based on Internet
   Protocol, IPv4 or IPv6 like a simple router.
 
+* :doc:`Network Layer 3 forwarding Graph<l3_forward_graph>`: The Network Layer3
+  forwarding Graph, or ``l3fwd_graph`` application does forwarding based on IPv4
+  like a simple router with DPDK Graph framework.
+
 * :doc:`Hardware packet copying<ioat>`: The Hardware packet copying,
   or ``ioatfwd`` application demonstrates how to use IOAT rawdev driver for
   copying packets between two threads.
diff --git a/doc/guides/sample_app_ug/l3_forward_graph.rst b/doc/guides/sample_app_ug/l3_forward_graph.rst
new file mode 100644
index 000000000..73153f82b
--- /dev/null
+++ b/doc/guides/sample_app_ug/l3_forward_graph.rst
@@ -0,0 +1,327 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+L3 Forwarding Graph Sample Application
+======================================
+
+The L3 Forwarding Graph application is a simple example of packet processing
+using the DPDK Graph framework. The application performs L3 forwarding using
+Graph framework and nodes written for graph framework.
+
+Overview
+--------
+
+The application demonstrates the use of the graph framework and graph nodes
+``ethdev_rx``, ``ip4_lookup``, ``ip4_rewrite``, ``ethdev_tx`` and ``pkt_drop`` in DPDK to
+implement packet forwarding.
+
+The initialization is very similar to those of the :doc:`l3_forward`.
+There is also additional initialization of graph for graph object creation
+and configuration per lcore.
+Run-time path is main thing that differs from L3 forwarding sample application.
+Difference is that forwarding logic starting from Rx, followed by LPM lookup,
+TTL update and finally Tx is implemented inside graph nodes. These nodes are
+interconnected in graph framework. Application main loop needs to walk over
+graph using ``rte_graph_walk()`` with graph objects created one per slave lcore.
+
+The lookup method is as per implementation of ``ip4_lookup`` graph node.
+The ID of the output interface for the input packet is the next hop returned by
+the LPM lookup. The set of LPM rules used by the application is statically
+configured and provided to ``ip4_lookup`` graph node and ``ip4_rewrite`` graph node
+using node control API ``rte_node_ip4_route_add()`` and ``rte_node_ip4_rewrite_add()``.
+
+In the sample application, only IPv4 forwarding is supported as of now.
+
+Compiling the Application
+-------------------------
+
+To compile the sample application see :doc:`compiling`.
+
+The application is located in the ``l3fwd-graph`` sub-directory.
+
+Running the Application
+-----------------------
+
+The application has a number of command line options similar to l3fwd::
+
+    ./l3fwd-graph [EAL options] -- -p PORTMASK
+                                   [-P]
+                                   --config(port,queue,lcore)[,(port,queue,lcore)]
+                                   [--eth-dest=X,MM:MM:MM:MM:MM:MM]
+                                   [--enable-jumbo [--max-pkt-len PKTLEN]]
+                                   [--no-numa]
+                                   [--per-port-pool]
+
+Where,
+
+* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
+
+* ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
+  Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
+
+* ``--config (port,queue,lcore)[,(port,queue,lcore)]:`` Determines which queues from which ports are mapped to which cores.
+
+* ``--eth-dest=X,MM:MM:MM:MM:MM:MM:`` Optional, ethernet destination for port X.
+
+* ``--enable-jumbo:`` Optional, enables jumbo frames.
+
+* ``--max-pkt-len:`` Optional, under the premise of enabling jumbo, maximum packet length in decimal (64-9600).
+
+* ``--no-numa:`` Optional, disables numa awareness.
+
+* ``--per-port-pool:`` Optional, set to use independent buffer pools per port. Without this option, single buffer pool is used for all ports.
+
+For example, consider a dual processor socket platform with 8 physical cores, where cores 0-7 and 16-23 appear on socket 0,
+while cores 8-15 and 24-31 appear on socket 1.
+
+To enable L3 forwarding between two ports, assuming that both ports are in the same socket, using two cores, cores 1 and 2,
+(which are in the same socket too), use the following command:
+
+.. code-block:: console
+
+    ./build/l3fwd-graph -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+
+In this command:
+
+*   The -l option enables cores 1, 2
+
+*   The -p option enables ports 0 and 1
+
+*   The --config option enables one queue on each port and maps each (port,queue) pair to a specific core.
+    The following table shows the mapping in this example:
+
++----------+-----------+-----------+-------------------------------------+
+| **Port** | **Queue** | **lcore** | **Description**                     |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 0        | 0         | 1         | Map queue 0 from port 0 to lcore 1. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 1        | 0         | 2         | Map queue 0 from port 1 to lcore 2. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+
+Refer to the *DPDK Getting Started Guide* for general information on running applications and
+the Environment Abstraction Layer (EAL) options.
+
+.. _l3_fwd_graph_explanation:
+
+Explanation
+-----------
+
+The following sections provide some explanation of the sample application code.
+As mentioned in the overview section, the initialization is similar to that of
+the :doc:`l3_forward`. Run-time path though similar in functionality to that of
+:doc:`l3_forward`, major part of the implementation is in graph nodes via used
+via ``librte_node`` library.
+The following sections describe aspects that are specific to the L3 Forwarding
+Graph sample application.
+
+Graph Node Pre-Init Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After device configuration and device Rx, Tx queue setup is complete,
+a minimal config of port id, num_rx_queues, num_tx_queues, mempools etc will
+be passed to *ethdev_** node ctrl API ``rte_node_eth_config()``. This will be
+lead to the clone of ``ethdev_rx`` and ``ethdev_tx`` nodes as ``ethdev_rx-X-Y`` and
+``ethdev_tx-X`` where X, Y represent port id and queue id associated with them.
+In case of ``ethdev_tx-X`` nodes, tx queue id assigned per instance of the node
+is same as graph id.
+
+These cloned nodes along with existing static nodes such as ``ip4_lookup`` and
+``ip4_rewrite`` will be used in graph creation to associate node's to lcore
+specific graph object.
+
+.. code-block:: c
+
+    RTE_ETH_FOREACH_DEV(portid)
+    {
+
+        /* ... */
+        ret = rte_eth_dev_configure(portid, nb_rx_queue,
+                                    n_tx_queue, &local_port_conf);
+        /* ... */
+
+        /* Init one TX queue per couple (lcore,port) */
+        queueid = 0;
+        for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+            /* ... */
+            ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+                                         socketid, txconf);
+            /* ... */
+            queueid++;
+        }
+
+        /* Setup ethdev node config */
+        ethdev_conf[nb_conf].port_id = portid;
+        ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+        ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+        if (!per_port_pool)
+            ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+        else
+          ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+        ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+        nb_conf++;
+        printf("\n");
+    }
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* Init RX queues */
+        for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+            /* ... */
+            if (!per_port_pool)
+                ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                             &rxq_conf, pktmbuf_pool[0][socketid]);
+            else
+              ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                           &rxq_conf, pktmbuf_pool[portid][socketid]);
+            /* ... */
+        }
+    }
+
+    /* Ethdev node config, skip rx queue mapping */
+    ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+
+Graph Initialization
+~~~~~~~~~~~~~~~~~~~~
+
+Now a graph needs to be created with a specific set of nodes for every lcore.
+A graph object returned after graph creation is a per lcore object and
+cannot be shared between lcores. Since ``ethdev_tx-X`` node is per port node,
+it can be associated with all the graphs created as all the lcores should have
+Tx capability for every port. But ``ethdev_rx-X-Y`` node is created per
+(port, rx_queue_id), so they should be associated with a graph based on
+the application argument ``--config`` specifying rx queue mapping to lcore.
+
+.. note::
+
+    The Graph creation will fail if the passed set of shell node pattern's
+    are not sufficient to meet their inter-dependency or even one node is not
+    found with a given regex node pattern.
+
+.. code-block:: c
+
+    static const char *node_patterns[] = {
+        "ip4*",
+        "ethdev_tx-*",
+        "pkt_drop",
+    };
+    uint16_t nb_patterns = RTE_DIM(node_patterns);
+
+    /* ... */
+
+    /* Create a graph object per lcore with common nodes and
+     * lcore specific nodes based on application arguments
+     */
+    memset(&graph_conf, 0, sizeof(graph_conf));
+
+    /* Common set of nodes in every lcore's graph object */
+    graph_conf.node_patterns = node_patterns;
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* ... */
+
+        /* Skip graph creation if no source exists */
+        if (!qconf->n_rx_queue)
+            continue;
+
+        /* Add rx node patterns of this lcore based on --config */
+        for (i = 0; i < qconf->n_rx_queue; i++) {
+            graph_conf.node_patterns[nb_patterns + i] =
+                                qconf->rx_queue_list[i].node_name;
+        }
+
+        graph_conf.nb_node_patterns = nb_patterns + i;
+        graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+        snprintf(qconf->name, sizeof(qconf->name), "worker_%u", lcore_id);
+
+        graph_id = rte_graph_create(qconf->name, &graph_conf);
+
+        /* ... */
+
+        qconf->graph = rte_graph_lookup(qconf->name);
+
+        /* ... */
+    }
+
+Forwarding data(Route, Next-Hop) addition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once graph objects are created, node specific info like routes and rewrite
+headers will be provided run-time using ``rte_node_ip4_route_add()`` and
+``rte_node_ip4_rewrite_add()`` API.
+
+.. note::
+
+    Since currently ``ip4_lookup`` and ``ip4_rewrite`` nodes don't support
+    lock-less mechanisms(RCU, etc) to add run-time forwarding data like route and
+    rewrite data, forwarding data is added before packet processing loop is
+    launched on slave lcore.
+
+.. code-block:: c
+
+    /* Add route to ip4 graph infra */
+    for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+        /* ... */
+
+        dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+        next_hop = i;
+
+        /* ... */
+        ret = rte_node_ip4_route_add(ipv4_l3fwd_lpm_route_array[i].ip,
+                                     ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+                                     RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+        /* ... */
+
+        memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+        /* Add next hop for a given destination */
+        ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+                                       rewrite_len, dst_port);
+
+        RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+                route_str, next_hop);
+    }
+
+Packet Forwarding using Graph Walk
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that all the device configurations are done, graph creations are done and
+forwarding data is updated with nodes, slave lcores will be launched with graph
+main loop. Graph main loop is very simple in the sense that it needs to
+continuously call a non-blocking API ``rte_graph_walk()`` with it's lcore
+specific graph object that was already created.
+
+.. note::
+
+    rte_graph_walk() will walk over all the sources nodes i.e ``ethdev_rx-X-Y``
+    associated with a given graph and Rx the available packets and enqueue them
+    to the following node ``ip4_lookup`` which then will enqueue them to ``ip4_rewrite``
+    node if LPM lookup succeeds. ``ip4_rewrite`` node then will update Ethernet header
+    as per next-hop data and transmit the packet via port 'Z' by enqueuing
+    to ``ethdev_tx-Z`` node instance in its graph object.
+
+.. code-block:: c
+
+    /* Main processing loop */
+    static int
+    graph_main_loop(void *conf)
+    {
+        // ...
+
+        lcore_id = rte_lcore_id();
+        qconf = &lcore_conf[lcore_id];
+        graph = qconf->graph;
+
+        RTE_LOG(INFO, L3FWD_GRAPH,
+                "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+                qconf->name, graph);
+
+        /* Walk over graph until signal to quit */
+        while (likely(!force_quit))
+            rte_graph_walk(graph);
+        return 0;
+    }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support jerinj
  2020-04-03  9:26       ` Wang, Xiao W
@ 2020-04-06 12:36       ` Andrzej Ostruszka
  2020-04-06 14:59         ` Jerin Jacob
  1 sibling, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-06 12:36 UTC (permalink / raw)
  To: dev

Hello Jerin

I've started reviewing this and will go patch-by-patch so some of the
comments might sound silly and/or unnecessary.

On 3/31/20 9:29 PM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Graph architecture abstracts the data processing functions as
> "node" and "link" them together to create a complex "graph" to enable
> reusable/modular data processing functions.
> 
> These APIs enables graph framework operations such as create, lookup,
> dump and destroy on graph and node operations such as clone,
> edge update, and edge shrink, etc. The API also allows creating the
> stats cluster to monitor per graph and per node stats.
> 
> This patch defines the public API for graph support.
> This patch also adds support for the build infrastructure and
> update the MAINTAINERS file for the graph subsystem.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
[...]
> diff --git a/lib/librte_graph/rte_graph.h b/lib/librte_graph/rte_graph.h
> new file mode 100644
> index 000000000..4bcf0a6e5
> --- /dev/null
> +++ b/lib/librte_graph/rte_graph.h
> @@ -0,0 +1,786 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2020 Marvell International Ltd.
> + */
> +
> +#ifndef _RTE_GRAPH_H_
> +#define _RTE_GRAPH_H_
> +
> +/**
> + * @file rte_graph.h
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice

I think this @warning doc at global level is enough - no need to repeat
it below (just keeping __rte_experimental should be fine).

> + *
> + * Graph architecture abstracts the data processing functions as
> + * "node" and "link" them together to create a complex "graph" to enable
> + * reusable/modular data processing functions.
> + *
> + * This API enables graph framework operations such as create, lookup,
> + * dump and destroy on graph and node operations such as clone,
> + * edge update, and edge shrink, etc. The API also allows to create the stats
> + * cluster to monitor per graph and per node stats.
> + *
> + */
> +
> +#include <stdbool.h>
> +#include <stdio.h>
> +
> +#include <rte_common.h>
> +#include <rte_compat.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#define RTE_GRAPH_NAMESIZE 64 /**< Max length of graph name. */
> +#define RTE_NODE_NAMESIZE 64  /**< Max length of node name. */
> +#define RTE_GRAPH_OFF_INVALID UINT32_MAX /**< Invalid graph offset. */
> +#define RTE_NODE_ID_INVALID UINT32_MAX   /**< Invalid node id. */
> +#define RTE_EDGE_ID_INVALID UINT16_MAX   /**< Invalid edge id. */
> +#define RTE_GRAPH_ID_INVALID UINT16_MAX  /**< Invalid graph id. */
> +#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL /**< Graph fence data. */
> +
> +typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
> +typedef uint32_t rte_node_t;       /**< Node id type. */
> +typedef uint16_t rte_edge_t;       /**< Edge id type. */
> +typedef uint16_t rte_graph_t;      /**< Graph id type. */

I would use 'id' somewhere in the name of these typedefs - e.g. seeing
rte_node_t in the code (without knowing what it is) I'd be guessing this
is a pointer to 'struct rte_node'.
So maybe 'rte_node_id' or if we stick with _t convention and
rte_node_id_t is too long then maybe simple rte_nid_t/rte_eid_t/rte_gid_t?

> +
> +/** Burst size in terms of log2 */
> +#if RTE_GRAPH_BURST_SIZE == 1
> +#define RTE_GRAPH_BURST_SIZE_LOG2 0  /**< Object burst size of 1. */
> +#elif RTE_GRAPH_BURST_SIZE == 2
> +#define RTE_GRAPH_BURST_SIZE_LOG2 1  /**< Object burst size of 2. */
> +#elif RTE_GRAPH_BURST_SIZE == 4
> +#define RTE_GRAPH_BURST_SIZE_LOG2 2  /**< Object burst size of 4. */
> +#elif RTE_GRAPH_BURST_SIZE == 8
> +#define RTE_GRAPH_BURST_SIZE_LOG2 3  /**< Object burst size of 8. */
> +#elif RTE_GRAPH_BURST_SIZE == 16
> +#define RTE_GRAPH_BURST_SIZE_LOG2 4  /**< Object burst size of 16. */
> +#elif RTE_GRAPH_BURST_SIZE == 32
> +#define RTE_GRAPH_BURST_SIZE_LOG2 5  /**< Object burst size of 32. */
> +#elif RTE_GRAPH_BURST_SIZE == 64
> +#define RTE_GRAPH_BURST_SIZE_LOG2 6  /**< Object burst size of 64. */
> +#elif RTE_GRAPH_BURST_SIZE == 128
> +#define RTE_GRAPH_BURST_SIZE_LOG2 7  /**< Object burst size of 128. */
> +#elif RTE_GRAPH_BURST_SIZE == 256
> +#define RTE_GRAPH_BURST_SIZE_LOG2 8  /**< Object burst size of 256. */
> +#else
> +#error "Unsupported burst size"

If other sizes are not supported then maybe it would be better to have
in options RTE_GRAPH_BURST_SIZE_LOG2 and define BURST_SIZE in terms of
this LOG2?

> +#endif
> +
> +/* Forward declaration */
> +struct rte_node;  /**< Node data */
> +struct rte_graph; /**< Graph data */

'data'?  Maybe 'object' or something like that.

[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Structure defines the node registration parameters.
> + *
> + * @see __rte_node_register(), RTE_NODE_REGISTER()
> + */
> +struct rte_node_register {
> +	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
> +	uint64_t flags;		      /**< Node configuration flag. */
> +#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
> +	rte_node_process_t process; /**< Node process function. */
> +	rte_node_init_t init;       /**< Node init function. */
> +	rte_node_fini_t fini;       /**< Node fini function. */
> +	rte_node_t id;		    /**< Node Identifier. */
> +	rte_node_t parent_id;       /**< Identifier of parent node. */
> +	rte_edge_t nb_edges;        /**< Number of edges from this node. */
> +	const char *next_nodes[];   /**< Names of next nodes. */

Not nodes ids?  It seems that basic handle for graph/node is its name -
not an id or pointer.  Is it so?  If so could you shed some light why it
is better/more convenient?  Late binding of nodes during graph creation?

> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Create Graph.
> + *
> + * Create memory reel, detect loops and find isolated nodes.
> + *
> + * @param name
> + *   Unique name for this graph.
> + * @param prm
> + *   Graph parameter, includes node names and count to be included
> + *   in this graph.
> + *
> + * @return
> + *   Unique graph id on success, RTE_GRAPH_ID_INVALID otherwise.
> + */
> +__rte_experimental
> +rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Destroy Graph.
> + *
> + * Free Graph memory reel.
> + *
> + * @param name
> + *   Name of the graph to destroy.
> + *
> + * @return
> + *   0 on success, error otherwise.
> + */
> +__rte_experimental
> +rte_graph_t rte_graph_destroy(const char *name);

Why rte_graph_t on return?  I have no experience with this but would
expect to have rte_graph_t (id) to be the handle via which graph is kept
(this is what rte_graph_create() returns) so would expect rte_graph_t to
be the input arg here.

[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Export the graph as graph viz dot file
> + *
> + * @param name
> + *   Name of the graph to export.
> + * @param f
> + *   File pointer to export the graph.
> + *
> + * @return
> + *   0 on success, error otherwise.
> + */
> +__rte_experimental
> +rte_graph_t rte_graph_export(const char *name, FILE *f);

Why rte_graph_t on return?

[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Get maximum number of graph available.
> + *
> + * @return
> + *   Maximum graph count on success, RTE_GRAPH_ID_INVALID otherwise.
> + */
> +__rte_experimental
> +rte_graph_t rte_graph_max_count(void);

Why rte_graph_t on return?  And why RTE_GRAPH_ID_IVALID can be returned?

[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Get node object with in graph from id.
> + *
> + * @param graph_id
> + *   Graph id to get node pointer from.
> + * @param node_id
> + *   Node id to get node pointer.
> + *
> + * @return
> + *   Node pointer on success, NULL otherwise.
> + */
> +__rte_experimental
> +struct rte_node *rte_graph_node_get(rte_graph_t graph_id, uint32_t node_id);

Maybe rte_node_t (or its changed name if you accept earlier comment)
instead of uint32_t?

[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Register new packet processing node. Nodes can be registered
> + * dynamically via this call or statically via the RTE_NODE_REGISTER
> + * macro.
> + *
> + * @param node
> + *   Valid node pointer with name, process function and next_nodes.
> + *
> + * @return
> + *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
> + *
> + * @see RTE_NODE_REGISTER()
> + */
> +__rte_experimental
> +rte_node_t __rte_node_register(const struct rte_node_register *node);
> +
> +/**
> + * Register a static node.
> + *
> + * The static node is registered through the constructor scheme, thereby, it can
> + * be used in a multi-process scenario.
> + *
> + * @param node
> + *   Valid node pointer with name, process function, and next_nodes.
> + */
> +#define RTE_NODE_REGISTER(node)                                                \
> +	RTE_INIT(rte_node_register_##node)                                     \
> +	{                                                                      \
> +		node.parent_id = RTE_NODE_ID_INVALID;                          \
> +		node.id = __rte_node_register(&node);                          \
> +	}

I would position rte_node_register struct definition near these so they
are grouped by "topic".

[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Get the number of edges for a node from node id.
> + *
> + * @param id
> + *   Valid node id.
> + *
> + * @return
> + *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
> + */
> +__rte_experimental
> +rte_edge_t rte_node_edge_count(rte_node_t id);

I would clarify here what edge is?  Incoming nodes, next-nodes or both.
 Why edge-id typedef on return and why EDGE_ID_INVALID returned.  Would
int/-EINVAL (for wrong 'id') be better?

> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Update the edges for a node from node id.
> + *
> + * @param id
> + *   Valid node id.
> + * @param from
> + *   Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
> + * in that case, it will be added to the end of the list.
> + * @param next_nodes
> + *   Name of the edges to update.
> + * @param nb_edges
> + *   Number of edges to update.
> + *
> + * @return
> + *   Valid edge count on success, 0 otherwise.
> + */
> +__rte_experimental
> +rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
> +				const char **next_nodes, uint16_t nb_edges);

So from this I infer that edge is either incoming or outgoing - right?

> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Shrink the edges to a given size.
> + *
> + * @param id
> + *   Valid node id.
> + * @param size
> + *   New size to shrink the edges.
> + *
> + * @return
> + *   New size on success, RTE_EDGE_ID_INVALID otherwise.
> + */
> +__rte_experimental
> +rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Get the edge names from a given node.
> + *
> + * @param id
> + *   Valid node id.
> + * @param[out] next_nodes
> + *   Buffer to copy the edge names. The NULL value is allowed in that case,
> + * the function returns the size of the array that needs to be allocated.
> + *
> + * @return
> + *   When next_nodes == NULL, it returns the size of the array else
> + *  number of item copied.
> + */
> +__rte_experimental
> +rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);

I guess this doesn't copy names just stores pointers to names.

> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Get maximum nodes created so far.
> + *
> + * @return
> + *   Maximum nodes count on success, 0 otherwise.
> + */
> +__rte_experimental
> +rte_node_t rte_node_max_count(void);

If this is "created so far" then why call it 'max'?  I guess this is max
possible so I would update description.

[...]

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph operation helpers
  2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph operation helpers jerinj
@ 2020-04-06 13:47       ` Wang, Xiao W
  2020-04-06 14:08         ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Wang, Xiao W @ 2020-04-06 13:47 UTC (permalink / raw)
  To: jerinj, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram

Hi,

Comment inline.

Best Regards,
Xiao

> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of jerinj@marvell.com
> Sent: Wednesday, April 1, 2020 3:29 AM
> To: Jerin Jacob <jerinj@marvell.com>; Kiran Kumar K
> <kirankumark@marvell.com>
> Cc: dev@dpdk.org; thomas@monjalon.net; david.marchand@redhat.com;
> mdr@ashroe.eu; mattias.ronnblom@ericsson.com;
> pbhagavatula@marvell.com; ndabilpuram@marvell.com
> Subject: [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph
> operation helpers
> 
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding internal graph API helpers support to check whether a graph has
> isolated nodes and any node have a loop to itself and BFS
> algorithm implementation etc.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
>  lib/librte_graph/Makefile        |   1 +
>  lib/librte_graph/graph.c         |   2 +-
>  lib/librte_graph/graph_ops.c     | 169 ++++++++++++++++++++++++++++++
>  lib/librte_graph/graph_private.h | 173 +++++++++++++++++++++++++++++++
>  lib/librte_graph/meson.build     |   2 +-
>  5 files changed, 345 insertions(+), 2 deletions(-)
>  create mode 100644 lib/librte_graph/graph_ops.c
> 
> diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
> index 2a6d86933..39ecb2652 100644
> --- a/lib/librte_graph/Makefile
> +++ b/lib/librte_graph/Makefile
> @@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
>  # all source are stored in SRCS-y
>  SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
>  SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
> +SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
>  SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
> 
>  # install header files
> diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
> index a9c124896..4c3f2fe7b 100644
> --- a/lib/librte_graph/graph.c
> +++ b/lib/librte_graph/graph.c
> @@ -7,7 +7,7 @@
>  #include "graph_private.h"
> 
>  static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
> -
> +int rte_graph_logtype;
>  void
>  graph_spinlock_lock(void)
>  {
> diff --git a/lib/librte_graph/graph_ops.c b/lib/librte_graph/graph_ops.c
> new file mode 100644
> index 000000000..335595311
> --- /dev/null
> +++ b/lib/librte_graph/graph_ops.c
> @@ -0,0 +1,169 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2020 Marvell International Ltd.
> + */
> +
> +#include <stdbool.h>
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_errno.h>
> +
> +#include "graph_private.h"
> +
> +/* Check whether a node has next_node to itself */
> +static inline int
> +node_has_loop_edge(struct node *node)
> +{
> +	rte_edge_t i;
> +	char *name;
> +	int rc = 0;
> +
> +	for (i = 0; i < node->nb_edges; i++) {
> +		if (strncmp(node->name, node->next_nodes[i],
> +			    RTE_NODE_NAMESIZE) == 0) {
> +			name = node->name;
> +			rc = 1;
> +			SET_ERR_JMP(EINVAL, fail, "Node %s has loop to
> self",
> +				    name);
> +		}
> +	}
> +fail:
> +	return rc;
> +}
> +
> +int
> +graph_node_has_loop_edge(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		if (node_has_loop_edge(graph_node->node))
> +			return 1;
> +
> +	return 0;
> +}
> +
> +rte_node_t
> +graph_src_nodes_count(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +	rte_node_t rc = 0;
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		if (graph_node->node->flags & RTE_NODE_SOURCE_F)
> +			rc++;
> +
> +	if (rc == 0)
> +		SET_ERR_JMP(EINVAL, fail, "Graph needs at least a source
> node");
> +fail:
> +	return rc;
> +}
> +
> +/* Check whether a node has next_node to a source node */
> +int
> +graph_node_has_edge_to_src_node(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +	struct node *node;
> +	rte_edge_t i;
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> +		for (i = 0; i < graph_node->node->nb_edges; i++) {
> +			node = graph_node->adjacency_list[i]->node;
> +			if (node->flags & RTE_NODE_SOURCE_F)
> +				SET_ERR_JMP(
> +					EEXIST, fail,
> +					"Node %s points to the source
> node %s",
> +					graph_node->node->name, node-
> >name);
> +		}
> +	}
> +
> +	return 0;
> +fail:
> +	return 1;
> +}
> +
> +rte_node_t
> +graph_nodes_count(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +	rte_node_t count = 0;
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		count++;
> +
> +	return count;
> +}
> +
> +void
> +graph_mark_nodes_as_not_visited(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		graph_node->visited = false;
> +}
> +
> +int
> +graph_bfs(struct graph *graph, struct graph_node *start)
> +{
> +	struct graph_node **queue, *v, *tmp;
> +	uint16_t head = 0, tail = 0;
> +	rte_edge_t i;
> +	size_t sz;
> +
> +	sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
> +	queue = malloc(sz);
> +	if (queue == NULL)
> +		SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue
> of %zu",
> +			    sz);
> +
> +	/* BFS algorithm */
> +	queue[tail++] = start;
> +	start->visited = true;
> +	while (head != tail) {
> +		v = queue[head++];
> +		for (i = 0; i < v->node->nb_edges; i++) {
> +			tmp = v->adjacency_list[i];
> +			if (tmp->visited == false) {
> +				queue[tail++] = tmp;
> +				tmp->visited = true;
> +			}
> +		}
> +	}
> +
> +	free(queue);
> +	return 0;
> +
> +fail:
> +	return -rte_errno;
> +}
> +
> +/* Check whether a node has connected path or parent node */
> +int
> +graph_has_isolated_node(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +
> +	graph_mark_nodes_as_not_visited(graph);
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> +		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
> +			if (graph_node->node->nb_edges == 0)
> +				SET_ERR_JMP(EINVAL, fail,
> +					    "%s node needs minimum one
> edge",
> +					    graph_node->node->name);
> +			if (graph_bfs(graph, graph_node))
> +				goto fail;
> +		}
> +	}
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		if (graph_node->visited == false)
> +			SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
> +				    graph_node->node->name);
> +
> +	return 0;
> +fail:
> +	return 1;
> +}
 Do you think we even need to detect loop which is neither self-looping nor looping-to-src,
or in another word, loop constructed by some intermediate nodes?

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph operation helpers
  2020-04-06 13:47       ` Wang, Xiao W
@ 2020-04-06 14:08         ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-06 14:08 UTC (permalink / raw)
  To: Wang, Xiao W
  Cc: jerinj, Kiran Kumar K, dev, thomas, david.marchand, mdr,
	mattias.ronnblom, pbhagavatula, ndabilpuram

> > +/* Check whether a node has connected path or parent node */
> > +int
> > +graph_has_isolated_node(struct graph *graph)
> > +{
> > +     struct graph_node *graph_node;
> > +
> > +     graph_mark_nodes_as_not_visited(graph);
> > +
> > +     STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> > +             if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
> > +                     if (graph_node->node->nb_edges == 0)
> > +                             SET_ERR_JMP(EINVAL, fail,
> > +                                         "%s node needs minimum one
> > edge",
> > +                                         graph_node->node->name);
> > +                     if (graph_bfs(graph, graph_node))
> > +                             goto fail;
> > +             }
> > +     }
> > +
> > +     STAILQ_FOREACH(graph_node, &graph->node_list, next)
> > +             if (graph_node->visited == false)
> > +                     SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
> > +                                 graph_node->node->name);
> > +
> > +     return 0;
> > +fail:
> > +     return 1;
> > +}
>  Do you think we even need to detect loop which is neither self-looping nor looping-to-src,
> or in another word, loop constructed by some intermediate nodes?

We support loop constructed by some intermediate nodes, example use
case would be in the IP in IP packet,
where process the tunnel and send the inner one the backward IP node.

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support
  2020-04-06 12:36       ` Andrzej Ostruszka
@ 2020-04-06 14:59         ` Jerin Jacob
  2020-04-06 16:09           ` Andrzej Ostruszka
  0 siblings, 1 reply; 219+ messages in thread
From: Jerin Jacob @ 2020-04-06 14:59 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dpdk-dev

On Mon, Apr 6, 2020 at 6:06 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
>
> Hello Jerin

Hello Andrzej,

>
> I've started reviewing this and will go patch-by-patch so some of the
> comments might sound silly and/or unnecessary.

Thanks for the comments.



> > +
> > +/**
> > + * @file rte_graph.h
> > + *
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
>
> I think this @warning doc at global level is enough - no need to repeat
> it below (just keeping __rte_experimental should be fine).

Will fix it v5.

>
> > + *
> > + * Graph architecture abstracts the data processing functions as
> > + * "node" and "link" them together to create a complex "graph" to enable
> > + * reusable/modular data processing functions.
> > + *
> > +
> > +typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
> > +typedef uint32_t rte_node_t;       /**< Node id type. */
> > +typedef uint16_t rte_edge_t;       /**< Edge id type. */
> > +typedef uint16_t rte_graph_t;      /**< Graph id type. */
>
> I would use 'id' somewhere in the name of these typedefs - e.g. seeing
> rte_node_t in the code (without knowing what it is) I'd be guessing this
> is a pointer to 'struct rte_node'.
> So maybe 'rte_node_id' or if we stick with _t convention and
> rte_node_id_t is too long then maybe simple rte_nid_t/rte_eid_t/rte_gid_t?

Considering typedef will not be pointers in Linux coding standard, I
have chosen shorter
name. considering eid, gid is cryptic and Since you think, rte_node_id
better, I will change to
that.


>
> > +
> > +/** Burst size in terms of log2 */
> > +#if RTE_GRAPH_BURST_SIZE == 1
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 0  /**< Object burst size of 1. */
> > +#elif RTE_GRAPH_BURST_SIZE == 2
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 1  /**< Object burst size of 2. */
> > +#elif RTE_GRAPH_BURST_SIZE == 4
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 2  /**< Object burst size of 4. */
> > +#elif RTE_GRAPH_BURST_SIZE == 8
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 3  /**< Object burst size of 8. */
> > +#elif RTE_GRAPH_BURST_SIZE == 16
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 4  /**< Object burst size of 16. */
> > +#elif RTE_GRAPH_BURST_SIZE == 32
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 5  /**< Object burst size of 32. */
> > +#elif RTE_GRAPH_BURST_SIZE == 64
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 6  /**< Object burst size of 64. */
> > +#elif RTE_GRAPH_BURST_SIZE == 128
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 7  /**< Object burst size of 128. */
> > +#elif RTE_GRAPH_BURST_SIZE == 256
> > +#define RTE_GRAPH_BURST_SIZE_LOG2 8  /**< Object burst size of 256. */
> > +#else
> > +#error "Unsupported burst size"
>
> If other sizes are not supported then maybe it would be better to have
> in options RTE_GRAPH_BURST_SIZE_LOG2 and define BURST_SIZE in terms of
> this LOG2?

Since RTE_GRAPH_BURST_SIZE used in fastpath, I thought of avoiding any operation
in the fastpath, even though the compiler most probably optimizes it.
The RTE_CACHE_LINE_SIZE also
taken a similar path.



>
> > +#endif
> > +
> > +/* Forward declaration */
> > +struct rte_node;  /**< Node data */
> > +struct rte_graph; /**< Graph data */
>
> 'data'?  Maybe 'object' or something like that.

Yes. I will change to data.

>
> [...]
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Structure defines the node registration parameters.
> > + *
> > + * @see __rte_node_register(), RTE_NODE_REGISTER()
> > + */
> > +struct rte_node_register {
> > +     char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
> > +     uint64_t flags;               /**< Node configuration flag. */
> > +#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
> > +     rte_node_process_t process; /**< Node process function. */
> > +     rte_node_init_t init;       /**< Node init function. */
> > +     rte_node_fini_t fini;       /**< Node fini function. */
> > +     rte_node_t id;              /**< Node Identifier. */
> > +     rte_node_t parent_id;       /**< Identifier of parent node. */
> > +     rte_edge_t nb_edges;        /**< Number of edges from this node. */
> > +     const char *next_nodes[];   /**< Names of next nodes. */
>
> Not nodes ids?  It seems that basic handle for graph/node is its name -
> not an id or pointer.  Is it so?  If so could you shed some light why it
> is better/more convenient?  Late binding of nodes during graph creation?

Yes. This node name can be filled in registration time when NODE ID is
not known.


> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Destroy Graph.
> > + *
> > + * Free Graph memory reel.
> > + *
> > + * @param name
> > + *   Name of the graph to destroy.
> > + *
> > + * @return
> > + *   0 on success, error otherwise.
> > + */
> > +__rte_experimental
> > +rte_graph_t rte_graph_destroy(const char *name);
>
> Why rte_graph_t on return?  I have no experience with this but would
> expect to have rte_graph_t (id) to be the handle via which graph is kept
> (this is what rte_graph_create() returns) so would expect rte_graph_t to
> be the input arg here.

In order to look API synergy, I have returned   rte_graph_t everywhere.
But you are right. I will change "int" and pass the ID.


>
> [...]
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Export the graph as graph viz dot file
> > + *
> > + * @param name
> > + *   Name of the graph to export.
> > + * @param f
> > + *   File pointer to export the graph.
> > + *
> > + * @return
> > + *   0 on success, error otherwise.
> > + */
> > +__rte_experimental
> > +rte_graph_t rte_graph_export(const char *name, FILE *f);
>
> Why rte_graph_t on return?

Will change to int.

>
> [...]
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Get maximum number of graph available.
> > + *
> > + * @return
> > + *   Maximum graph count on success, RTE_GRAPH_ID_INVALID otherwise.
> > + */
> > +__rte_experimental
> > +rte_graph_t rte_graph_max_count(void);
>
> Why rte_graph_t on return?  And why RTE_GRAPH_ID_IVALID can be returned?

Graph node ID is expressed in rte_graph_t.
Yes RTE_GRAPH_ID_IVALID can not be returned, I will remove it.

>
> [...]
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Get node object with in graph from id.
> > + *
> > + * @param graph_id
> > + *   Graph id to get node pointer from.
> > + * @param node_id
> > + *   Node id to get node pointer.
> > + *
> > + * @return
> > + *   Node pointer on success, NULL otherwise.
> > + */
> > +__rte_experimental
> > +struct rte_node *rte_graph_node_get(rte_graph_t graph_id, uint32_t node_id);
>
> Maybe rte_node_t (or its changed name if you accept earlier comment)
> instead of uint32_t?

Yes. I will change to rte_node_id_t.

>
> [...]
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Register new packet processing node. Nodes can be registered
> > + * dynamically via this call or statically via the RTE_NODE_REGISTER
> > + * macro.
> > + *
> > + * @param node
> > + *   Valid node pointer with name, process function and next_nodes.
> > + *
> > + * @return
> > + *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
> > + *
> > + * @see RTE_NODE_REGISTER()
> > + */
> > +__rte_experimental
> > +rte_node_t __rte_node_register(const struct rte_node_register *node);
> > +
> > +/**
> > + * Register a static node.
> > + *
> > + * The static node is registered through the constructor scheme, thereby, it can
> > + * be used in a multi-process scenario.
> > + *
> > + * @param node
> > + *   Valid node pointer with name, process function, and next_nodes.
> > + */
> > +#define RTE_NODE_REGISTER(node)                                                \
> > +     RTE_INIT(rte_node_register_##node)                                     \
> > +     {                                                                      \
> > +             node.parent_id = RTE_NODE_ID_INVALID;                          \
> > +             node.id = __rte_node_register(&node);                          \
> > +     }
>
> I would position rte_node_register struct definition near these so they
> are grouped by "topic".

No strong opinion on this. I will take your suggestion.

>
> [...]
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Get the number of edges for a node from node id.
> > + *
> > + * @param id
> > + *   Valid node id.
> > + *
> > + * @return
> > + *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
> > + */
> > +__rte_experimental
> > +rte_edge_t rte_node_edge_count(rte_node_t id);
>
> I would clarify here what edge is?  Incoming nodes, next-nodes or both.

It is next-node. I will update the doc.

>  Why edge-id typedef on return and why EDGE_ID_INVALID returned.  Would
> int/-EINVAL (for wrong 'id') be better?

Edge node ID is expressed in rte_edge_id_t. SO, I think, it fine to return
rte_edge_id_t. "This would avoid any compassion issue as well."


>
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Update the edges for a node from node id.
> > + *
> > + * @param id
> > + *   Valid node id.
> > + * @param from
> > + *   Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
> > + * in that case, it will be added to the end of the list.
> > + * @param next_nodes
> > + *   Name of the edges to update.
> > + * @param nb_edges
> > + *   Number of edges to update.
> > + *
> > + * @return
> > + *   Valid edge count on success, 0 otherwise.
> > + */
> > +__rte_experimental
> > +rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
> > +                             const char **next_nodes, uint16_t nb_edges);
>
> So from this I infer that edge is either incoming or outgoing - right?

Next node.

>
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Shrink the edges to a given size.
> > + *
> > + * @param id
> > + *   Valid node id.
> > + * @param size
> > + *   New size to shrink the edges.
> > + *
> > + * @return
> > + *   New size on success, RTE_EDGE_ID_INVALID otherwise.
> > + */
> > +__rte_experimental
> > +rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Get the edge names from a given node.
> > + *
> > + * @param id
> > + *   Valid node id.
> > + * @param[out] next_nodes
> > + *   Buffer to copy the edge names. The NULL value is allowed in that case,
> > + * the function returns the size of the array that needs to be allocated.
> > + *
> > + * @return
> > + *   When next_nodes == NULL, it returns the size of the array else
> > + *  number of item copied.
> > + */
> > +__rte_experimental
> > +rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);
>
> I guess this doesn't copy names just stores pointers to names.

Copy the names too.

>
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Get maximum nodes created so far.
> > + *
> > + * @return
> > + *   Maximum nodes count on success, 0 otherwise.
> > + */
> > +__rte_experimental
> > +rte_node_t rte_node_max_count(void);
>
> If this is "created so far" then why call it 'max'?  I guess this is max
> possible so I would update description.

Yes. I will change to max possible in the description.


>
> [...]
>
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support
  2020-04-06 14:59         ` Jerin Jacob
@ 2020-04-06 16:09           ` Andrzej Ostruszka
  2020-04-07 10:27             ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-06 16:09 UTC (permalink / raw)
  To: Jerin Jacob; +Cc: dpdk-dev

On 4/6/20 4:59 PM, Jerin Jacob wrote:
> On Mon, Apr 6, 2020 at 6:06 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
[...]
>>> +typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
>>> +typedef uint32_t rte_node_t;       /**< Node id type. */
>>> +typedef uint16_t rte_edge_t;       /**< Edge id type. */
>>> +typedef uint16_t rte_graph_t;      /**< Graph id type. */
>>
>> I would use 'id' somewhere in the name of these typedefs - e.g. seeing
>> rte_node_t in the code (without knowing what it is) I'd be guessing this
>> is a pointer to 'struct rte_node'.
>> So maybe 'rte_node_id' or if we stick with _t convention and
>> rte_node_id_t is too long then maybe simple rte_nid_t/rte_eid_t/rte_gid_t?
> 
> Considering typedef will not be pointers in Linux coding standard, I
> have chosen shorter
> name. considering eid, gid is cryptic and Since you think, rte_node_id
> better, I will change to
> that.

If the typedef are not pointers by the coding standard then I'm fine
with current names - no need to change.

[...]
>> [...]
>>> +/**
>>> + * @warning
>>> + * @b EXPERIMENTAL: this API may change without prior notice
>>> + *
>>> + * Get the number of edges for a node from node id.
>>> + *
>>> + * @param id
>>> + *   Valid node id.
>>> + *
>>> + * @return
>>> + *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
>>> + */
>>> +__rte_experimental
>>> +rte_edge_t rte_node_edge_count(rte_node_t id);
>>
>> I would clarify here what edge is?  Incoming nodes, next-nodes or both.
> 
> It is next-node. I will update the doc.
> 
>>  Why edge-id typedef on return and why EDGE_ID_INVALID returned.  Would
>> int/-EINVAL (for wrong 'id') be better?
> 
> Edge node ID is expressed in rte_edge_id_t. SO, I think, it fine to return
> rte_edge_id_t. "This would avoid any compassion issue as well."

Did not understand the last sentence.  Could you rephrase it?

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 03/29] graph: implement node operations
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 03/29] graph: implement node operations jerinj
@ 2020-04-06 17:57         ` Andrzej Ostruszka
  2020-04-07  2:43           ` [dpdk-dev] [EXT] " Kiran Kumar Kokkilagadda
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-06 17:57 UTC (permalink / raw)
  To: jerinj, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
[...]
> diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
> index 336cd1c94..d04a0fce0 100644
> --- a/lib/librte_graph/node.c
> +++ b/lib/librte_graph/node.c
[...]
> +static rte_edge_t
> +edge_update(struct node *node, struct node *prev, rte_edge_t from,
> +	    const char **next_nodes, rte_edge_t nb_edges)
> +{
> +	rte_edge_t i, max_edges, count = 0;
> +	struct node *new_node;
> +	bool need_realloc;
> +	size_t sz;
> +
> +	if (from == RTE_EDGE_ID_INVALID)
> +		from = node->nb_edges;
> +
> +	/* Don't create hole in next_nodes[] list */
> +	if (from > node->nb_edges) {
> +		rte_errno = ENOMEM;
> +		goto fail;
> +	}
> +
> +	/* Remove me from list */
> +	STAILQ_REMOVE(&node_list, node, node, next);
> +
> +	/* Allocate the storage space for new node if required */
> +	max_edges = from + nb_edges;
> +	need_realloc = max_edges > node->nb_edges;
> +	if (need_realloc) {
> +		sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
> +		new_node = realloc(node, sz);
> +		if (new_node == NULL) {
> +			rte_errno = ENOMEM;
> +			goto restore;
> +		} else {
> +			node = new_node;
> +		}
> +	}
> +
> +	/* Update the new nodes name */
> +	for (i = from; i < max_edges; i++, count++) {
> +		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
> +				RTE_NODE_NAMESIZE) < 0) {
> +			rte_errno = E2BIG;
> +			goto restore;
> +		}
> +	}
> +restore:
> +	/* Update the linked list to point new node address in prev node */
> +	if (prev)
> +		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
> +	else
> +		STAILQ_INSERT_HEAD(&node_list, node, next);

AFAIU node_list keeps the list of nodes - so I guess you wanted here
"replace" the old node pointer with the new one.  I have not yet seen
the usage of this function but it seems to me like you unconditionally
insert the updated node - possibly having node pointer present doubly or
with stale pointer.  I might be missing something here.

> +
> +	if (need_realloc)
> +		node->nb_edges += max_edges;

It looks to me like this should be simple '='.

> +
> +fail:
> +	return count;
> +}
[...]
> +rte_edge_t
> +rte_node_edge_update(rte_node_t id, rte_edge_t from, const char **next_nodes,
> +		     uint16_t nb_edges)
> +{
> +	rte_edge_t rc = RTE_EDGE_ID_INVALID;
> +	struct node *n, *prev;
> +
> +	NODE_ID_CHECK(id);
> +	graph_spinlock_lock();
> +
> +	prev = NULL;
> +	STAILQ_FOREACH(n, &node_list, next) {
> +		if (n->id == id) {
> +			rc = edge_update(n, prev, from, next_nodes, nb_edges);
> +			break;
> +		}
> +		prev = n;
> +	}

OK so in this context my comment above seems to be valid.  When we find
the id we have: prev -> n, we call update() and in there we insert
new_node after prev so we end up with: prev -> n' -> n where n' might be
new address for n or just n when no realloc was performed.

Do I miss anything?

> +
> +	graph_spinlock_unlock();
> +fail:
> +	return rc;
> +}
> +
> +static rte_node_t
> +node_copy_edges(struct node *node, char *next_nodes[])
> +{
> +	rte_edge_t i;
> +
> +	for (i = 0; i < node->nb_edges; i++)
> +		next_nodes[i] = node->next_nodes[i];
> +
> +	return i;
> +}
> +
> +rte_node_t
> +rte_node_edge_get(rte_node_t id, char *next_nodes[])
> +{
> +	rte_node_t rc = RTE_NODE_ID_INVALID;
> +	struct node *node;
> +
> +	NODE_ID_CHECK(id);
> +	graph_spinlock_lock();
> +
> +	STAILQ_FOREACH(node, &node_list, next) {
> +		if (node->id == id) {
> +			if (next_nodes == NULL)
> +				rc = sizeof(char *) * node->nb_edges;
> +			else
> +				rc = node_copy_edges(node, next_nodes);

Do we want to ready for next_nodes not large enough?

> +			break;
> +		}
> +	}
> +
> +	graph_spinlock_unlock();
> +fail:
> +	return rc;
> +}

[...]

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines jerinj
@ 2020-04-06 18:17         ` Andrzej Ostruszka
  2020-04-07 10:22           ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-06 18:17 UTC (permalink / raw)
  To: jerinj, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding node debug API implementation support to dump
> single or all the node objects to the given file.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
[...]
> diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
> index d04a0fce0..8592c1221 100644
> --- a/lib/librte_graph/node.c
> +++ b/lib/librte_graph/node.c
> @@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
>  	return rc;
>  }
>  
> +static void
> +node_scan_dump(FILE *f, rte_node_t id, bool all)
> +{
> +	struct node *node;
> +
> +	RTE_ASSERT(f != NULL);

Why the assert?  Below this is used in public (I guess) functions so
user can provide wrong input - in that case I'd expect warning/error not
an assert.

> +	NODE_ID_CHECK(id);
> +
> +	STAILQ_FOREACH(node, &node_list, next) {
> +		if (all == true) {
> +			node_dump(f, node);
> +		} else if (node->id == id) {
> +			node_dump(f, node);
> +			return;
> +		}
> +	}
> +fail:
> +	return;
> +}
> +
> +void
> +rte_node_dump(FILE *f, rte_node_t id)
> +{
> +	node_scan_dump(f, id, false);
> +}
> +
> +void
> +rte_node_list_dump(FILE *f)
> +{
> +	node_scan_dump(f, 0, true);
> +}
> +
[...]

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v4 03/29] graph: implement node operations
  2020-04-06 17:57         ` Andrzej Ostruszka
@ 2020-04-07  2:43           ` Kiran Kumar Kokkilagadda
  2020-04-07  8:47             ` Andrzej Ostruszka
  2020-04-07 10:20             ` Jerin Jacob
  0 siblings, 2 replies; 219+ messages in thread
From: Kiran Kumar Kokkilagadda @ 2020-04-07  2:43 UTC (permalink / raw)
  To: Andrzej Ostruszka, Jerin Jacob Kollanukkaran
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom,
	Pavan Nikhilesh Bhagavatula, Nithin Kumar Dabilpuram,
	xiao.w.wang



> -----Original Message-----
> From: Andrzej Ostruszka <amo@semihalf.com>
> Sent: Monday, April 6, 2020 11:27 PM
> To: Jerin Jacob Kollanukkaran <jerinj@marvell.com>; Kiran Kumar Kokkilagadda
> <kirankumark@marvell.com>
> Cc: dev@dpdk.org; thomas@monjalon.net; david.marchand@redhat.com;
> mdr@ashroe.eu; mattias.ronnblom@ericsson.com; Pavan Nikhilesh
> Bhagavatula <pbhagavatula@marvell.com>; Nithin Kumar Dabilpuram
> <ndabilpuram@marvell.com>; xiao.w.wang@intel.com
> Subject: [EXT] Re: [dpdk-dev] [PATCH v4 03/29] graph: implement node
> operations
> 
> External Email
> 
> ----------------------------------------------------------------------
> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> [...]
> > diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c index
> > 336cd1c94..d04a0fce0 100644
> > --- a/lib/librte_graph/node.c
> > +++ b/lib/librte_graph/node.c
> [...]
> > +static rte_edge_t
> > +edge_update(struct node *node, struct node *prev, rte_edge_t from,
> > +	    const char **next_nodes, rte_edge_t nb_edges) {
> > +	rte_edge_t i, max_edges, count = 0;
> > +	struct node *new_node;
> > +	bool need_realloc;
> > +	size_t sz;
> > +
> > +	if (from == RTE_EDGE_ID_INVALID)
> > +		from = node->nb_edges;
> > +
> > +	/* Don't create hole in next_nodes[] list */
> > +	if (from > node->nb_edges) {
> > +		rte_errno = ENOMEM;
> > +		goto fail;
> > +	}
> > +
> > +	/* Remove me from list */
> > +	STAILQ_REMOVE(&node_list, node, node, next);

This is where we will remove the node first unconditionally. Later we update the new node.

> > +
> > +	/* Allocate the storage space for new node if required */
> > +	max_edges = from + nb_edges;
> > +	need_realloc = max_edges > node->nb_edges;
> > +	if (need_realloc) {
> > +		sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
> > +		new_node = realloc(node, sz);
> > +		if (new_node == NULL) {
> > +			rte_errno = ENOMEM;
> > +			goto restore;
> > +		} else {
> > +			node = new_node;
> > +		}
> > +	}
> > +
> > +	/* Update the new nodes name */
> > +	for (i = from; i < max_edges; i++, count++) {
> > +		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
> > +				RTE_NODE_NAMESIZE) < 0) {
> > +			rte_errno = E2BIG;
> > +			goto restore;
> > +		}
> > +	}
> > +restore:
> > +	/* Update the linked list to point new node address in prev node */
> > +	if (prev)
> > +		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
> > +	else
> > +		STAILQ_INSERT_HEAD(&node_list, node, next);
> 
> AFAIU node_list keeps the list of nodes - so I guess you wanted here "replace"
> the old node pointer with the new one.  I have not yet seen the usage of this
> function but it seems to me like you unconditionally insert the updated node -
> possibly having node pointer present doubly or with stale pointer.  I might be
> missing something here.
> 
See above.

> > +
> > +	if (need_realloc)
> > +		node->nb_edges += max_edges;
> 
> It looks to me like this should be simple '='.
> 
> > +
> > +fail:
> > +	return count;
> > +}
> [...]
> > +rte_edge_t
> > +rte_node_edge_update(rte_node_t id, rte_edge_t from, const char
> **next_nodes,
> > +		     uint16_t nb_edges)
> > +{
> > +	rte_edge_t rc = RTE_EDGE_ID_INVALID;
> > +	struct node *n, *prev;
> > +
> > +	NODE_ID_CHECK(id);
> > +	graph_spinlock_lock();
> > +
> > +	prev = NULL;
> > +	STAILQ_FOREACH(n, &node_list, next) {
> > +		if (n->id == id) {
> > +			rc = edge_update(n, prev, from, next_nodes,
> nb_edges);
> > +			break;
> > +		}
> > +		prev = n;
> > +	}
> 
> OK so in this context my comment above seems to be valid.  When we find the id
> we have: prev -> n, we call update() and in there we insert new_node after prev
> so we end up with: prev -> n' -> n where n' might be new address for n or just n
> when no realloc was performed.
> 
> Do I miss anything?
> 

See above.

> > +
> > +	graph_spinlock_unlock();
> > +fail:
> > +	return rc;
> > +}
> > +
> > +static rte_node_t
> > +node_copy_edges(struct node *node, char *next_nodes[]) {
> > +	rte_edge_t i;
> > +
> > +	for (i = 0; i < node->nb_edges; i++)
> > +		next_nodes[i] = node->next_nodes[i];
> > +
> > +	return i;
> > +}
> > +
> > +rte_node_t
> > +rte_node_edge_get(rte_node_t id, char *next_nodes[]) {
> > +	rte_node_t rc = RTE_NODE_ID_INVALID;
> > +	struct node *node;
> > +
> > +	NODE_ID_CHECK(id);
> > +	graph_spinlock_lock();
> > +
> > +	STAILQ_FOREACH(node, &node_list, next) {
> > +		if (node->id == id) {
> > +			if (next_nodes == NULL)
> > +				rc = sizeof(char *) * node->nb_edges;
> > +			else
> > +				rc = node_copy_edges(node, next_nodes);
> 
> Do we want to ready for next_nodes not large enough?
> 
> > +			break;
> > +		}
> > +	}
> > +
> > +	graph_spinlock_unlock();
> > +fail:
> > +	return rc;
> > +}
> 
> [...]
> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v4 03/29] graph: implement node operations
  2020-04-07  2:43           ` [dpdk-dev] [EXT] " Kiran Kumar Kokkilagadda
@ 2020-04-07  8:47             ` Andrzej Ostruszka
  2020-04-07 10:20             ` Jerin Jacob
  1 sibling, 0 replies; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-07  8:47 UTC (permalink / raw)
  To: Kiran Kumar Kokkilagadda, Jerin Jacob Kollanukkaran
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom,
	Pavan Nikhilesh Bhagavatula, Nithin Kumar Dabilpuram,
	xiao.w.wang

On 4/7/20 4:43 AM, Kiran Kumar Kokkilagadda wrote:
[...]
>>> +static rte_edge_t
>>> +edge_update(struct node *node, struct node *prev, rte_edge_t from,
>>> +	    const char **next_nodes, rte_edge_t nb_edges) {
>>> +	rte_edge_t i, max_edges, count = 0;
>>> +	struct node *new_node;
>>> +	bool need_realloc;
>>> +	size_t sz;
>>> +
>>> +	if (from == RTE_EDGE_ID_INVALID)
>>> +		from = node->nb_edges;
>>> +
>>> +	/* Don't create hole in next_nodes[] list */
>>> +	if (from > node->nb_edges) {
>>> +		rte_errno = ENOMEM;
>>> +		goto fail;
>>> +	}
>>> +
>>> +	/* Remove me from list */
>>> +	STAILQ_REMOVE(&node_list, node, node, next);
> 
> This is where we will remove the node first unconditionally. Later we update the new node.

Thanks Kiran, don't know how I missed that :)

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v4 03/29] graph: implement node operations
  2020-04-07  2:43           ` [dpdk-dev] [EXT] " Kiran Kumar Kokkilagadda
  2020-04-07  8:47             ` Andrzej Ostruszka
@ 2020-04-07 10:20             ` Jerin Jacob
  1 sibling, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-07 10:20 UTC (permalink / raw)
  To: Kiran Kumar Kokkilagadda
  Cc: Andrzej Ostruszka, Jerin Jacob Kollanukkaran, dev, thomas,
	david.marchand, mdr, mattias.ronnblom,
	Pavan Nikhilesh Bhagavatula, Nithin Kumar Dabilpuram,
	xiao.w.wang

>
> > > +
> > > +   if (need_realloc)
> > > +           node->nb_edges += max_edges;
> >
> > It looks to me like this should be simple '='.

Yes. Will fix in v5

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines
  2020-04-06 18:17         ` Andrzej Ostruszka
@ 2020-04-07 10:22           ` Jerin Jacob
  2020-04-07 11:50             ` Andrzej Ostruszka
  0 siblings, 1 reply; 219+ messages in thread
From: Jerin Jacob @ 2020-04-07 10:22 UTC (permalink / raw)
  To: Andrzej Ostruszka
  Cc: Jerin Jacob, Kiran Kumar K, dpdk-dev, Thomas Monjalon,
	David Marchand, Ray Kinsella, Mattias Rönnblom,
	Pavan Nikhilesh, Nithin Dabilpuram, Xiao Wang

On Mon, Apr 6, 2020 at 11:47 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
>
> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> > From: Jerin Jacob <jerinj@marvell.com>
> >
> > Adding node debug API implementation support to dump
> > single or all the node objects to the given file.
> >
> > Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> [...]
> > diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
> > index d04a0fce0..8592c1221 100644
> > --- a/lib/librte_graph/node.c
> > +++ b/lib/librte_graph/node.c
> > @@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
> >       return rc;
> >  }
> >
> > +static void
> > +node_scan_dump(FILE *f, rte_node_t id, bool all)
> > +{
> > +     struct node *node;
> > +
> > +     RTE_ASSERT(f != NULL);
>
> Why the assert?  Below this is used in public (I guess) functions so
> user can provide wrong input - in that case I'd expect warning/error not
> an assert.

Public API rte_node_dump() and node_scan_dump() calls this API without
any check.

>
> > +     NODE_ID_CHECK(id);
> > +
> > +     STAILQ_FOREACH(node, &node_list, next) {
> > +             if (all == true) {
> > +                     node_dump(f, node);
> > +             } else if (node->id == id) {
> > +                     node_dump(f, node);
> > +                     return;
> > +             }
> > +     }
> > +fail:
> > +     return;
> > +}
> > +
> > +void
> > +rte_node_dump(FILE *f, rte_node_t id)
> > +{
> > +     node_scan_dump(f, id, false);
> > +}
> > +
> > +void
> > +rte_node_list_dump(FILE *f)
> > +{
> > +     node_scan_dump(f, 0, true);
> > +}
> > +
> [...]
>
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support
  2020-04-06 16:09           ` Andrzej Ostruszka
@ 2020-04-07 10:27             ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-07 10:27 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dpdk-dev

On Mon, Apr 6, 2020 at 9:39 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
>
> On 4/6/20 4:59 PM, Jerin Jacob wrote:
> > On Mon, Apr 6, 2020 at 6:06 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
> [...]
> >>> +typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
> >>> +typedef uint32_t rte_node_t;       /**< Node id type. */
> >>> +typedef uint16_t rte_edge_t;       /**< Edge id type. */
> >>> +typedef uint16_t rte_graph_t;      /**< Graph id type. */
> >>
> >> I would use 'id' somewhere in the name of these typedefs - e.g. seeing
> >> rte_node_t in the code (without knowing what it is) I'd be guessing this
> >> is a pointer to 'struct rte_node'.
> >> So maybe 'rte_node_id' or if we stick with _t convention and
> >> rte_node_id_t is too long then maybe simple rte_nid_t/rte_eid_t/rte_gid_t?
> >
> > Considering typedef will not be pointers in Linux coding standard, I
> > have chosen shorter
> > name. considering eid, gid is cryptic and Since you think, rte_node_id
> > better, I will change to
> > that.
>
> If the typedef are not pointers by the coding standard then I'm fine
> with current names - no need to change.

Ack.

>
> [...]
> >> [...]
> >>> +/**
> >>> + * @warning
> >>> + * @b EXPERIMENTAL: this API may change without prior notice
> >>> + *
> >>> + * Get the number of edges for a node from node id.
> >>> + *
> >>> + * @param id
> >>> + *   Valid node id.
> >>> + *
> >>> + * @return
> >>> + *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
> >>> + */
> >>> +__rte_experimental
> >>> +rte_edge_t rte_node_edge_count(rte_node_t id);
> >>
> >> I would clarify here what edge is?  Incoming nodes, next-nodes or both.
> >
> > It is next-node. I will update the doc.
> >
> >>  Why edge-id typedef on return and why EDGE_ID_INVALID returned.  Would
> >> int/-EINVAL (for wrong 'id') be better?
> >
> > Edge node ID is expressed in rte_edge_id_t. SO, I think, it fine to return
> > rte_edge_id_t. "This would avoid any compassion issue as well."
>
> Did not understand the last sentence.  Could you rephrase it?

Sorry. I meant signed vs unsigned comparison if needed in the
variable. ie if (int < uint32_t)

>
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines
  2020-04-07 10:22           ` Jerin Jacob
@ 2020-04-07 11:50             ` Andrzej Ostruszka
  2020-04-07 12:09               ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-07 11:50 UTC (permalink / raw)
  To: Jerin Jacob
  Cc: Jerin Jacob, Kiran Kumar K, dpdk-dev, Thomas Monjalon,
	David Marchand, Ray Kinsella, Mattias Rönnblom,
	Pavan Nikhilesh, Nithin Dabilpuram, Xiao Wang

On 4/7/20 12:22 PM, Jerin Jacob wrote:
> On Mon, Apr 6, 2020 at 11:47 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
>>
>> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
>>> From: Jerin Jacob <jerinj@marvell.com>
>>>
>>> Adding node debug API implementation support to dump
>>> single or all the node objects to the given file.
>>>
>>> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
>>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
>>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
>>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
>> [...]
>>> diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
>>> index d04a0fce0..8592c1221 100644
>>> --- a/lib/librte_graph/node.c
>>> +++ b/lib/librte_graph/node.c
>>> @@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
>>>       return rc;
>>>  }
>>>
>>> +static void
>>> +node_scan_dump(FILE *f, rte_node_t id, bool all)
>>> +{
>>> +     struct node *node;
>>> +
>>> +     RTE_ASSERT(f != NULL);
>>
>> Why the assert?  Below this is used in public (I guess) functions so
>> user can provide wrong input - in that case I'd expect warning/error not
>> an assert.
> 
> Public API rte_node_dump() and node_scan_dump() calls this API without
> any check.

That was my point.  I would expect either there or here to have a check
for arg instead of assert.  I'd say that asserts are very good for
checking internal logic, but not so for checking if user input is OK.

But I'm fine if you ignore this.

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines
  2020-04-07 11:50             ` Andrzej Ostruszka
@ 2020-04-07 12:09               ` Jerin Jacob
  2020-04-07 12:50                 ` Andrzej Ostruszka
  0 siblings, 1 reply; 219+ messages in thread
From: Jerin Jacob @ 2020-04-07 12:09 UTC (permalink / raw)
  To: Andrzej Ostruszka
  Cc: Jerin Jacob, Kiran Kumar K, dpdk-dev, Thomas Monjalon,
	David Marchand, Ray Kinsella, Mattias Rönnblom,
	Pavan Nikhilesh, Nithin Dabilpuram, Xiao Wang

On Tue, Apr 7, 2020 at 5:20 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
>
> On 4/7/20 12:22 PM, Jerin Jacob wrote:
> > On Mon, Apr 6, 2020 at 11:47 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
> >>
> >> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> >>> From: Jerin Jacob <jerinj@marvell.com>
> >>>
> >>> Adding node debug API implementation support to dump
> >>> single or all the node objects to the given file.
> >>>
> >>> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> >>> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> >>> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> >>> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> >> [...]
> >>> diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
> >>> index d04a0fce0..8592c1221 100644
> >>> --- a/lib/librte_graph/node.c
> >>> +++ b/lib/librte_graph/node.c
> >>> @@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
> >>>       return rc;
> >>>  }
> >>>
> >>> +static void
> >>> +node_scan_dump(FILE *f, rte_node_t id, bool all)
> >>> +{
> >>> +     struct node *node;
> >>> +
> >>> +     RTE_ASSERT(f != NULL);
> >>
> >> Why the assert?  Below this is used in public (I guess) functions so
> >> user can provide wrong input - in that case I'd expect warning/error not
> >> an assert.
> >
> > Public API rte_node_dump() and node_scan_dump() calls this API without
> > any check.
>
> That was my point.  I would expect either there or here to have a check
> for arg instead of assert.  I'd say that asserts are very good for
> checking internal logic, but not so for checking if user input is OK.

All DPDK _dump() functions returns void. I thought, We will keep the same here.
Another option is. if it is NULL we can return.
i.e
-RTE_ASSERT(f != NULL);
+if (f == NULL)
+ return

Either scheme is OK with me, Let me know your preference, I will
change accordingly.

>
> But I'm fine if you ignore this.
>
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers jerinj
@ 2020-04-07 12:16         ` Andrzej Ostruszka
  2020-04-07 12:27           ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-07 12:16 UTC (permalink / raw)
  To: dev

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding internal graph API helpers support to check whether a graph has
> isolated nodes and any node have a loop to itself and BFS
> algorithm implementation etc.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
[...]> +/* Check whether a node has next_node to itself */
> +static inline int
> +node_has_loop_edge(struct node *node)
> +{
> +	rte_edge_t i;
> +	char *name;
> +	int rc = 0;
> +
> +	for (i = 0; i < node->nb_edges; i++) {
> +		if (strncmp(node->name, node->next_nodes[i],
> +			    RTE_NODE_NAMESIZE) == 0) {
> +			name = node->name;
> +			rc = 1;
> +			SET_ERR_JMP(EINVAL, fail, "Node %s has loop to self",
> +				    name);
> +		}
> +	}
> +fail:
> +	return rc;
> +}

In general, I'd expect such warnings/errors to be at usage - this is
simple test and its job should be just return true/false.  But in this
particular case I guess it is always an error (no cycles in graph
allowed) so I'm fine if you leave it here.

> +
> +int
> +graph_node_has_loop_edge(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		if (node_has_loop_edge(graph_node->node))
> +			return 1;
> +
> +	return 0;
> +}
[...]
> +void
> +graph_mark_nodes_as_not_visited(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		graph_node->visited = false;
> +}
> +
> +int
> +graph_bfs(struct graph *graph, struct graph_node *start)
> +{
> +	struct graph_node **queue, *v, *tmp;
> +	uint16_t head = 0, tail = 0;
> +	rte_edge_t i;
> +	size_t sz;
> +
> +	sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
> +	queue = malloc(sz);
> +	if (queue == NULL)
> +		SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
> +			    sz);
> +
> +	/* BFS algorithm */
> +	queue[tail++] = start;
> +	start->visited = true;
> +	while (head != tail) {
> +		v = queue[head++];
> +		for (i = 0; i < v->node->nb_edges; i++) {
> +			tmp = v->adjacency_list[i];
> +			if (tmp->visited == false) {
> +				queue[tail++] = tmp;
> +				tmp->visited = true;
> +			}
> +		}
> +	}
> +
> +	free(queue);
> +	return 0;
> +
> +fail:
> +	return -rte_errno;
> +}

What is the purpose of this function?  It looks like just marking as
visited.  Then maybe change the name to graph_mark_bfs() or something
like that.

> +
> +/* Check whether a node has connected path or parent node */
> +int
> +graph_has_isolated_node(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +
> +	graph_mark_nodes_as_not_visited(graph);
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> +		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
> +			if (graph_node->node->nb_edges == 0)
> +				SET_ERR_JMP(EINVAL, fail,
> +					    "%s node needs minimum one edge",
> +					    graph_node->node->name);
> +			if (graph_bfs(graph, graph_node))
> +				goto fail;
> +		}
> +	}
> +
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		if (graph_node->visited == false)
> +			SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
> +				    graph_node->node->name);> +
> +	return 0;

You don't want to clear visited because it will not be used or cleared
on next call?

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers
  2020-04-07 12:16         ` Andrzej Ostruszka
@ 2020-04-07 12:27           ` Jerin Jacob
  2020-04-07 12:54             ` Andrzej Ostruszka
  0 siblings, 1 reply; 219+ messages in thread
From: Jerin Jacob @ 2020-04-07 12:27 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dpdk-dev

On Tue, Apr 7, 2020 at 5:46 PM Andrzej Ostruszka <amo@semihalf.com> wrote:

> > +int
> > +graph_bfs(struct graph *graph, struct graph_node *start)
> > +{
> > +     struct graph_node **queue, *v, *tmp;
> > +     uint16_t head = 0, tail = 0;
> > +     rte_edge_t i;
> > +     size_t sz;
> > +
> > +     sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
> > +     queue = malloc(sz);
> > +     if (queue == NULL)
> > +             SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
> > +                         sz);
> > +
> > +     /* BFS algorithm */
> > +     queue[tail++] = start;
> > +     start->visited = true;
> > +     while (head != tail) {
> > +             v = queue[head++];
> > +             for (i = 0; i < v->node->nb_edges; i++) {
> > +                     tmp = v->adjacency_list[i];
> > +                     if (tmp->visited == false) {
> > +                             queue[tail++] = tmp;
> > +                             tmp->visited = true;
> > +                     }
> > +             }
> > +     }
> > +
> > +     free(queue);
> > +     return 0;
> > +
> > +fail:
> > +     return -rte_errno;
> > +}
>
> What is the purpose of this function?  It looks like just marking as
> visited.  Then maybe change the name to graph_mark_bfs() or something
> like that.

graph_ops.c has all generic graph-related functions.
BFS(Breadth-First Search) is a generic graph operation. The primitive
can be used for various other graph operations.
IMO, It is better to avoid connecting with a marking using case in the
function name.


>
> > +
> > +/* Check whether a node has connected path or parent node */
> > +int
> > +graph_has_isolated_node(struct graph *graph)
> > +{
> > +     struct graph_node *graph_node;
> > +
> > +     graph_mark_nodes_as_not_visited(graph);

See below,

> > +
> > +     STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> > +             if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
> > +                     if (graph_node->node->nb_edges == 0)
> > +                             SET_ERR_JMP(EINVAL, fail,
> > +                                         "%s node needs minimum one edge",
> > +                                         graph_node->node->name);
> > +                     if (graph_bfs(graph, graph_node))
> > +                             goto fail;
> > +             }
> > +     }
> > +
> > +     STAILQ_FOREACH(graph_node, &graph->node_list, next)
> > +             if (graph_node->visited == false)
> > +                     SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
> > +                                 graph_node->node->name);> +
> > +     return 0;
>
> You don't want to clear visited because it will not be used or cleared
> on next call?

See above graph_mark_nodes_as_not_visited() function.

>
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines
  2020-04-07 12:09               ` Jerin Jacob
@ 2020-04-07 12:50                 ` Andrzej Ostruszka
  0 siblings, 0 replies; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-07 12:50 UTC (permalink / raw)
  To: Jerin Jacob
  Cc: Jerin Jacob, Kiran Kumar K, dpdk-dev, Thomas Monjalon,
	David Marchand, Ray Kinsella, Mattias Rönnblom,
	Pavan Nikhilesh, Nithin Dabilpuram, Xiao Wang

On 4/7/20 2:09 PM, Jerin Jacob wrote:
> On Tue, Apr 7, 2020 at 5:20 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
>>
>> On 4/7/20 12:22 PM, Jerin Jacob wrote:
[...]
>>>>> +static void
>>>>> +node_scan_dump(FILE *f, rte_node_t id, bool all)
>>>>> +{
>>>>> +     struct node *node;
>>>>> +
>>>>> +     RTE_ASSERT(f != NULL);
>>>>
>>>> Why the assert?  Below this is used in public (I guess) functions so
>>>> user can provide wrong input - in that case I'd expect warning/error not
>>>> an assert.
>>>
>>> Public API rte_node_dump() and node_scan_dump() calls this API without
>>> any check.
>>
>> That was my point.  I would expect either there or here to have a check
>> for arg instead of assert.  I'd say that asserts are very good for
>> checking internal logic, but not so for checking if user input is OK.
> 
> All DPDK _dump() functions returns void. I thought, We will keep the same here.
> Another option is. if it is NULL we can return.
> i.e
> -RTE_ASSERT(f != NULL);
> +if (f == NULL)
> + return
> 
> Either scheme is OK with me, Let me know your preference, I will
> change accordingly.

No, I don't want to silently skip that.  Have an error message here and
return error but don't call rte_panic() which would abort application.
That would be my preference, but if you don't want to change return type
here (and where this is called) then I'm fine with what it is now.

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers
  2020-04-07 12:27           ` Jerin Jacob
@ 2020-04-07 12:54             ` Andrzej Ostruszka
  2020-04-07 13:31               ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-07 12:54 UTC (permalink / raw)
  To: Jerin Jacob; +Cc: dpdk-dev

On 4/7/20 2:27 PM, Jerin Jacob wrote:
> On Tue, Apr 7, 2020 at 5:46 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
> 
>>> +int
>>> +graph_bfs(struct graph *graph, struct graph_node *start)
>>> +{
>>> +     struct graph_node **queue, *v, *tmp;
>>> +     uint16_t head = 0, tail = 0;
>>> +     rte_edge_t i;
>>> +     size_t sz;
>>> +
>>> +     sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
>>> +     queue = malloc(sz);
>>> +     if (queue == NULL)
>>> +             SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
>>> +                         sz);
>>> +
>>> +     /* BFS algorithm */
>>> +     queue[tail++] = start;
>>> +     start->visited = true;
>>> +     while (head != tail) {
>>> +             v = queue[head++];
>>> +             for (i = 0; i < v->node->nb_edges; i++) {
>>> +                     tmp = v->adjacency_list[i];
>>> +                     if (tmp->visited == false) {
>>> +                             queue[tail++] = tmp;
>>> +                             tmp->visited = true;
>>> +                     }
>>> +             }
>>> +     }
>>> +
>>> +     free(queue);
>>> +     return 0;
>>> +
>>> +fail:
>>> +     return -rte_errno;
>>> +}
>>
>> What is the purpose of this function?  It looks like just marking as
>> visited.  Then maybe change the name to graph_mark_bfs() or something
>> like that.
> 
> graph_ops.c has all generic graph-related functions.
> BFS(Breadth-First Search) is a generic graph operation. The primitive
> can be used for various other graph operations.
> IMO, It is better to avoid connecting with a marking using case in the
> function name.
> 
> 
>>
>>> +
>>> +/* Check whether a node has connected path or parent node */
>>> +int
>>> +graph_has_isolated_node(struct graph *graph)
>>> +{
>>> +     struct graph_node *graph_node;
>>> +
>>> +     graph_mark_nodes_as_not_visited(graph);
> 
> See below,
> 
>>> +
>>> +     STAILQ_FOREACH(graph_node, &graph->node_list, next) {
>>> +             if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
>>> +                     if (graph_node->node->nb_edges == 0)
>>> +                             SET_ERR_JMP(EINVAL, fail,
>>> +                                         "%s node needs minimum one edge",
>>> +                                         graph_node->node->name);
>>> +                     if (graph_bfs(graph, graph_node))
>>> +                             goto fail;
>>> +             }
>>> +     }
>>> +
>>> +     STAILQ_FOREACH(graph_node, &graph->node_list, next)
>>> +             if (graph_node->visited == false)
>>> +                     SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
>>> +                                 graph_node->node->name);> +
>>> +     return 0;
>>
>> You don't want to clear visited because it will not be used or cleared
>> on next call?
> 
> See above graph_mark_nodes_as_not_visited() function.

Yes I noticed that and referred to it in the question.  My intention was
to ask whether you are fine with graph having visited=true for the rest
of its life, or should we clear them again at the end of this function.

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers
  2020-04-07 12:54             ` Andrzej Ostruszka
@ 2020-04-07 13:31               ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-07 13:31 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dpdk-dev

On Tue, Apr 7, 2020 at 6:24 PM Andrzej Ostruszka <amo@semihalf.com> wrote:

> >
> >>> +
> >>> +     STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> >>> +             if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
> >>> +                     if (graph_node->node->nb_edges == 0)
> >>> +                             SET_ERR_JMP(EINVAL, fail,
> >>> +                                         "%s node needs minimum one edge",
> >>> +                                         graph_node->node->name);
> >>> +                     if (graph_bfs(graph, graph_node))
> >>> +                             goto fail;
> >>> +             }
> >>> +     }
> >>> +
> >>> +     STAILQ_FOREACH(graph_node, &graph->node_list, next)
> >>> +             if (graph_node->visited == false)
> >>> +                     SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
> >>> +                                 graph_node->node->name);> +
> >>> +     return 0;
> >>
> >> You don't want to clear visited because it will not be used or cleared
> >> on next call?
> >
> > See above graph_mark_nodes_as_not_visited() function.
>
> Yes I noticed that and referred to it in the question.  My intention was
> to ask whether you are fine with graph having visited=true for the rest
> of its life, or should we clear them again at the end of this function.

Got it. For now, visted=true is OK for the rest of it its life.

Since it needs to go over all the nodes to clear it again. As an optimization,
I thought of exposing  graph_mark_nodes_as_not_visited() and
 graph_bfs() is exported in graph_private.h. Those primitives would be enough
to make other use cases when needed.


>
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 07/29] graph: implement create and destroy APIs
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 07/29] graph: implement create and destroy APIs jerinj
@ 2020-04-08 16:57         ` Andrzej Ostruszka
  2020-04-08 17:23           ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-08 16:57 UTC (permalink / raw)
  To: dev

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding graph specific API implementations like graph create
> and graph destroy. This detect loops in the graph,
> check for isolated nodes and operation to verify the validity of
> graph.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
>  lib/librte_graph/graph.c               | 320 +++++++++++++++++++++++++
>  lib/librte_graph/rte_graph_version.map |   2 +
>  2 files changed, 322 insertions(+)
> 
> diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
> index e1930b7d2..dc373231e 100644
> --- a/lib/librte_graph/graph.c
> +++ b/lib/librte_graph/graph.c
[...]
> +static int
> +graph_node_add(struct graph *graph, struct node *node)
> +{
> +	struct graph_node *graph_node;
> +	size_t sz;
> +
> +	/* Skip the duplicate nodes */
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next)
> +		if (strncmp(node->name, graph_node->node->name,
> +			    RTE_NODE_NAMESIZE) == 0)

Is it not a "deficiency" of a program to attempt to add node twice?
If it is, then maybe a warning here?

[...]
> +rte_graph_t
> +rte_graph_create(const char *name, struct rte_graph_param *prm)
> +{
> +	struct graph *graph;
> +	const char *pattern;
> +	uint16_t i;
> +
> +	graph_spinlock_lock();
> +
> +	/* Check arguments sanity */
> +	if (prm == NULL)
> +		SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
> +
> +	if (name == NULL)
> +		SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
> +
> +	/* Check for existence of duplicate graph */
> +	STAILQ_FOREACH(graph, &graph_list, next)
> +		if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
> +			SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
> +				    name);
> +
> +	/* Create graph object */
> +	graph = calloc(1, sizeof(*graph));
> +	if (graph == NULL)
> +		SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
> +
> +	/* Initialize the graph object */
> +	STAILQ_INIT(&graph->node_list);
> +	if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
> +		SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
> +
> +	/* Expand node pattern and add the nodes to the graph */
> +	for (i = 0; i < prm->nb_node_patterns; i++) {
> +		pattern = prm->node_patterns[i];
> +		if (expand_pattern_to_node(graph, pattern))
> +			goto graph_cleanup;
> +	}
> +
> +	/* Go over all the nodes edges and add them to the graph */
> +	if (graph_node_edges_add(graph))
> +		goto graph_cleanup;
> +
> +	/* Update adjacency list of all nodes in the graph */
> +	if (graph_adjacency_list_update(graph))
> +		goto graph_cleanup;
> +
> +	/* Make sure at least a source node present in the graph */
> +	if (!graph_src_nodes_count(graph))
> +		goto graph_cleanup;
> +
> +	/* Make sure no node is pointing to source node */
> +	if (graph_node_has_edge_to_src_node(graph))
> +		goto graph_cleanup;
> +
> +	/* Don't allow node has loop to self */
> +	if (graph_node_has_loop_edge(graph))
> +		goto graph_cleanup;
> +
> +	/* Do BFS from src nodes on the graph to find isolated nodes */
> +	if (graph_has_isolated_node(graph))
> +		goto graph_cleanup;
> +
> +	/* Initialize graph object */
> +	graph->socket = prm->socket_id;
> +	graph->src_node_count = graph_src_nodes_count(graph);

Maybe reuse value of previous call (above)?

> +	graph->node_count = graph_nodes_count(graph);
> +	graph->id = graph_id;
> +
> +	/* Allocate the Graph fast path memory and populate the data */
> +	if (graph_fp_mem_create(graph))
> +		goto graph_cleanup;
> +
> +	/* Call init() of the all the nodes in the graph */
> +	if (graph_node_init(graph))
> +		goto graph_mem_destroy;
> +
> +	/* All good, Lets add the graph to the list */
> +	graph_id++;
> +	STAILQ_INSERT_TAIL(&graph_list, graph, next);
> +
> +	graph_spinlock_unlock();
> +	return graph->id;
> +
> +graph_mem_destroy:
> +	graph_fp_mem_destroy(graph);
> +graph_cleanup:
> +	graph_cleanup(graph);
> +free:
> +	free(graph);
> +fail:
> +	graph_spinlock_unlock();
> +	return RTE_GRAPH_ID_INVALID;
> +}
> +

With regards
Andrzej Ostruszka


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 07/29] graph: implement create and destroy APIs
  2020-04-08 16:57         ` Andrzej Ostruszka
@ 2020-04-08 17:23           ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-08 17:23 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dpdk-dev

> > +++ b/lib/librte_graph/graph.c
> [...]
> > +static int
> > +graph_node_add(struct graph *graph, struct node *node)
> > +{
> > +     struct graph_node *graph_node;
> > +     size_t sz;
> > +
> > +     /* Skip the duplicate nodes */
> > +     STAILQ_FOREACH(graph_node, &graph->node_list, next)
> > +             if (strncmp(node->name, graph_node->node->name,
> > +                         RTE_NODE_NAMESIZE) == 0)
>
> Is it not a "deficiency" of a program to attempt to add node twice?
> If it is, then maybe a warning here?

The library takes care of adding nodes the graph
- when user-specified
OR
- when a node depends on another node (gets the info from next nodes)
and it not specified by the user

So this internal function may be called with the same node.


>
> [...]
> > +rte_graph_t
> > +rte_graph_create(const char *name, struct rte_graph_param *prm)
> > +{
> > +     struct graph *graph;
> > +     const char *pattern;
> > +     uint16_t i;
> > +

> > +
> > +     /* Do BFS from src nodes on the graph to find isolated nodes */
> > +     if (graph_has_isolated_node(graph))
> > +             goto graph_cleanup;
> > +
> > +     /* Initialize graph object */
> > +     graph->socket = prm->socket_id;
> > +     graph->src_node_count = graph_src_nodes_count(graph);
>
> Maybe reuse value of previous call (above)?

Yep. I will change it in v5.

>
> > +     graph->node_count = graph_nodes_count(graph);
> > +     graph->id = graph_id;
> > +
> > +     /* Allocate the Graph fast path memory and populate the data */
> > +     if (graph_fp_mem_create(graph))
> > +             goto graph_cleanup;
> > +
> > +     /* Call init() of the all the nodes in the graph */
> > +     if (graph_node_init(graph))
> > +             goto graph_mem_destroy;
> > +
> > +     /* All good, Lets add the graph to the list */
> > +     graph_id++;
> > +     STAILQ_INSERT_TAIL(&graph_list, graph, next);
> > +
> > +     graph_spinlock_unlock();
> > +     return graph->id;
> > +
> > +graph_mem_destroy:
> > +     graph_fp_mem_destroy(graph);
> > +graph_cleanup:
> > +     graph_cleanup(graph);
> > +free:
> > +     free(graph);
> > +fail:
> > +     graph_spinlock_unlock();
> > +     return RTE_GRAPH_ID_INVALID;
> > +}
> > +
>
> With regards
> Andrzej Ostruszka
>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for graph reel
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for graph reel jerinj
@ 2020-04-08 17:30         ` Andrzej Ostruszka
  2020-04-09  2:44           ` Kiran Kumar Kokkilagadda
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-08 17:30 UTC (permalink / raw)
  To: dev

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
[...]
> diff --git a/lib/librte_graph/graph_populate.c b/lib/librte_graph/graph_populate.c
> new file mode 100644
> index 000000000..093512efa
> --- /dev/null
> +++ b/lib/librte_graph/graph_populate.c
> @@ -0,0 +1,234 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2020 Marvell International Ltd.
> + */
> +
> +#include <fnmatch.h>
> +#include <stdbool.h>
> +
> +#include <rte_common.h>
> +#include <rte_errno.h>
> +#include <rte_malloc.h>
> +#include <rte_memzone.h>
> +
> +#include "graph_private.h"
> +
> +static size_t
> +graph_fp_mem_calc_size(struct graph *graph)
> +{
> +	struct graph_node *graph_node;
> +	rte_node_t val;
> +	size_t sz;
> +
> +	/* Graph header */
> +	sz = sizeof(struct rte_graph);
> +	/* Source nodes list */
> +	sz += sizeof(rte_graph_off_t) * graph->src_node_count;
> +	/* Circular buffer for pending streams of size number of nodes */
> +	val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
> +	sz = RTE_ALIGN(sz, val);
> +	graph->cir_start = sz;
> +	graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
> +	sz += val;

Aren't here source nodes counted twice?  I'm trying now to wrap my head
around how this all is structured and laid out in memory (thus the
slowdown in review) so I am most probably missing something here.

> +	/* Fence */
> +	sz += sizeof(RTE_GRAPH_FENCE);
> +	sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
> +	graph->nodes_start = sz;
> +	/* For 0..N node objects with fence */
> +	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> +		sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
> +		sz += sizeof(struct rte_node);
> +		/* Pointer to next nodes(edges) */
> +		sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
> +	}
> +
> +	graph->mem_sz = sz;
> +	return sz;
> +}
> +
> +static void
> +graph_header_popluate(struct graph *_graph)
> +{
> +	struct rte_graph *graph = _graph->graph;
> +
> +	graph->tail = 0;
> +	graph->head = (int32_t)-_graph->src_node_count;
> +	graph->cir_mask = _graph->cir_mask;
> +	graph->nb_nodes = _graph->node_count;
> +	graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
> +	graph->nodes_start = _graph->nodes_start;
> +	graph->socket = _graph->socket;
> +	graph->id = _graph->id;
> +	memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);

As I've mentioned above I'm learning the structure of the lib/memory so
quick question here.  My understanding is that rte_graph is a "view of
the 'struct graph' sufficient for worker" so does it need both id &
name?  Both of them seems to be used in error or dump/debug paths.  It
probably doesn't matter (e.g. for performance) - just asking because
'id' seems to be used only in one place (where name could replace it
probably).

> +	graph->fence = RTE_GRAPH_FENCE;
> +}
> +
> +static void
> +graph_nodes_populate(struct graph *_graph)
> +{
> +	rte_graph_off_t off = _graph->nodes_start;
> +	struct rte_graph *graph = _graph->graph;
> +	struct graph_node *graph_node;
> +	rte_edge_t count, nb_edges;
> +	const char *parent;
> +	rte_node_t pid;
> +
> +	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
> +		struct rte_node *node = RTE_PTR_ADD(graph, off);
> +		memset(node, 0, sizeof(*node));
> +		node->fence = RTE_GRAPH_FENCE;
> +		node->off = off;
> +		node->process = graph_node->node->process;
> +		memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
> +		pid = graph_node->node->parent_id;
> +		if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
> +			parent = rte_node_id_to_name(pid);
> +			memcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);
> +		}
> +		node->id = graph_node->node->id;
> +		node->parent_id = pid;
> +		nb_edges = graph_node->node->nb_edges;
> +		node->nb_edges = nb_edges;
> +		off += sizeof(struct rte_node);
> +		/* Copy the name in first pass to replace with rte_node* later*/
> +		for (count = 0; count < nb_edges; count++)
> +			node->nodes[count] = (struct rte_node *)&graph_node
> +						     ->adjacency_list[count]
> +						     ->node->name[0];

I'm not sure I understand what is going here.  Please see below ...

> +
> +		off += sizeof(struct rte_node *) * nb_edges;
> +		off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
> +		node->next = off;
> +		__rte_node_stream_alloc(graph, node);
> +	}
> +}
[...]
> +static int
> +graph_node_nexts_populate(struct graph *_graph)
> +{
> +	rte_node_t count, val;
> +	rte_graph_off_t off;
> +	struct rte_node *node;
> +	const struct rte_graph *graph = _graph->graph;
> +	const char *name;
> +
> +	rte_graph_foreach_node(count, off, graph, node) {
> +		for (val = 0; val < node->nb_edges; val++) {
> +			name = (const char *)node->nodes[val];
> +			node->nodes[val] = graph_node_name_to_ptr(graph, name);

... Is it so that during node the first loop above some node might refer
(by name) to other node that is not yet "registered" so instead of
storing rte_node pointer you stored actually pointer to name which you
now update to proper rte_node?

> +			if (node->nodes[val] == NULL)
> +				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
> +		}
> +	}
> +
> +	return 0;
> +fail:
> +	return -rte_errno;
> +}
[...]

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 08/29] graph: implement graph operation APIs
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 08/29] graph: implement graph operation APIs jerinj
@ 2020-04-08 17:49         ` Andrzej Ostruszka
  2020-04-08 19:18           ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-08 17:49 UTC (permalink / raw)
  To: dev

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding support for graph specific API implementation like
> Graph lookup to get graph object, retrieving graph ID
> From name and graph name from ID.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
>  lib/librte_graph/graph.c               | 131 +++++++++++++++++++++++++
>  lib/librte_graph/rte_graph_version.map |   8 ++
>  2 files changed, 139 insertions(+)
> 
> diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
> index dc373231e..7c6a7897d 100644
> --- a/lib/librte_graph/graph.c
> +++ b/lib/librte_graph/graph.c
> @@ -210,6 +210,54 @@ graph_node_fini(struct graph *graph)
>  						       graph_node->node->name));
>  }
>  
> +static struct rte_graph *
> +graph_mem_fixup_node_ctx(struct rte_graph *graph)

How is this function fixing context?  It only updates process pointer
never touching context.

> +{
> +	struct rte_node *node;
> +	struct node *node_db;
> +	rte_graph_off_t off;
> +	rte_node_t count;
> +	const char *name;
> +
> +	rte_graph_foreach_node(count, off, graph, node) {
> +		if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
> +			name = node->name;
> +		else /* Cloned node */
> +			name = node->parent;
> +
> +		node_db = node_from_name(name);
> +		if (node_db == NULL)
> +			SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
> +		node->process = node_db->process;
> +	}
> +
> +	return graph;
> +fail:
> +	return NULL;
> +}
> +
> +static struct rte_graph *
> +graph_mem_fixup_secondray(struct rte_graph *graph)

Typo here?

> +{
> +	if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
> +		return graph;
> +
> +	return graph_mem_fixup_node_ctx(graph);
> +}
> +
> +struct rte_graph *
> +rte_graph_lookup(const char *name)
> +{
> +	const struct rte_memzone *mz;
> +	struct rte_graph *rc = NULL;
> +
> +	mz = rte_memzone_lookup(name);
> +	if (mz)
> +		rc = mz->addr;
> +
> +	return graph_mem_fixup_secondray(rc);
> +}
[...]

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 08/29] graph: implement graph operation APIs
  2020-04-08 17:49         ` Andrzej Ostruszka
@ 2020-04-08 19:18           ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-08 19:18 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dpdk-dev

On Wed, Apr 8, 2020 at 11:19 PM Andrzej Ostruszka <amo@semihalf.com> wrote:
>
> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> > From: Jerin Jacob <jerinj@marvell.com>
> >
> > Adding support for graph specific API implementation like
> > Graph lookup to get graph object, retrieving graph ID
> > From name and graph name from ID.
> >
> > Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > ---
> >  lib/librte_graph/graph.c               | 131 +++++++++++++++++++++++++
> >  lib/librte_graph/rte_graph_version.map |   8 ++
> >  2 files changed, 139 insertions(+)
> >
> > diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
> > index dc373231e..7c6a7897d 100644
> > --- a/lib/librte_graph/graph.c
> > +++ b/lib/librte_graph/graph.c
> > @@ -210,6 +210,54 @@ graph_node_fini(struct graph *graph)
> >                                                      graph_node->node->name));
> >  }
> >
> > +static struct rte_graph *
> > +graph_mem_fixup_node_ctx(struct rte_graph *graph)
>
> How is this function fixing context?  It only updates process pointer
> never touching context.

In the multiprocess model,

# The primary process( using rte_graph_create() API), it will create
the graph object
in hugepage memory
# Secondary process calls rte_graph_lookup() to get the graph object, created
by the primary process. Internally it uses rte_memzone_lookup() to get
the hugepage memory
populated by the primary process.
# The ONLY data that need to be updated in the secondary process context is the
process() function pointer. graph_mem_fixup_node_ctx(), do name-based
lookup with
node name and replace with function pointer in secondary process context.

>
> > +{
> > +     struct rte_node *node;
> > +     struct node *node_db;
> > +     rte_graph_off_t off;
> > +     rte_node_t count;
> > +     const char *name;
> > +
> > +     rte_graph_foreach_node(count, off, graph, node) {
> > +             if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
> > +                     name = node->name;
> > +             else /* Cloned node */
> > +                     name = node->parent;
> > +
> > +             node_db = node_from_name(name);
> > +             if (node_db == NULL)
> > +                     SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
> > +             node->process = node_db->process;
> > +     }
> > +
> > +     return graph;
> > +fail:
> > +     return NULL;
> > +}
> > +
> > +static struct rte_graph *
> > +graph_mem_fixup_secondray(struct rte_graph *graph)
>
> Typo here?

I will fix it v5.

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for graph reel
  2020-04-08 17:30         ` Andrzej Ostruszka
@ 2020-04-09  2:44           ` Kiran Kumar Kokkilagadda
  0 siblings, 0 replies; 219+ messages in thread
From: Kiran Kumar Kokkilagadda @ 2020-04-09  2:44 UTC (permalink / raw)
  To: Andrzej Ostruszka, dev



> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of Andrzej Ostruszka
> Sent: Wednesday, April 8, 2020 11:00 PM
> To: dev@dpdk.org
> Subject: Re: [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for
> graph reel
> 
> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> > From: Jerin Jacob <jerinj@marvell.com>
> [...]
> > diff --git a/lib/librte_graph/graph_populate.c
> > b/lib/librte_graph/graph_populate.c
> > new file mode 100644
> > index 000000000..093512efa
> > --- /dev/null
> > +++ b/lib/librte_graph/graph_populate.c
> > @@ -0,0 +1,234 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(C) 2020 Marvell International Ltd.
> > + */
> > +
> > +#include <fnmatch.h>
> > +#include <stdbool.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_errno.h>
> > +#include <rte_malloc.h>
> > +#include <rte_memzone.h>
> > +
> > +#include "graph_private.h"
> > +
> > +static size_t
> > +graph_fp_mem_calc_size(struct graph *graph) {
> > +	struct graph_node *graph_node;
> > +	rte_node_t val;
> > +	size_t sz;
> > +
> > +	/* Graph header */
> > +	sz = sizeof(struct rte_graph);
> > +	/* Source nodes list */
> > +	sz += sizeof(rte_graph_off_t) * graph->src_node_count;
> > +	/* Circular buffer for pending streams of size number of nodes */
> > +	val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
> > +	sz = RTE_ALIGN(sz, val);
> > +	graph->cir_start = sz;
> > +	graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
> > +	sz += val;
> 
> Aren't here source nodes counted twice?  I'm trying now to wrap my head
> around how this all is structured and laid out in memory (thus the slowdown in
> review) so I am most probably missing something here.
> 

Yes, we are counting source nodes offset, 2 times in the circular buffer. In fact intentionally we are allocating the circular buffer more than the required size (rte_align32pow2).
By allocating circular buffer with more size, at least in some cases we can avoid wraparound.
Let me try to explain how this memory reel and graph walk works.
This is how memory reel looks like.

1. Graph_header---> 2. FENCE ---> 3. [Graph walk always starts from here] memory for source node object offsets ---> 4. [circular buffer starts] enqueued node object offset [ circular buffer end] --> 5. FENCE ---> 6. Memory for Node objects

3 and 4 will have the offset of their corresponding node object in the 6.

Initially before graph walk start we will populate the 3 (see graph_src_nodes_populate) and when the graph walk start first we will go over 3 and based on the enqueues , we will populate the 4 and this is where we are creating circle (we will be keep walking in 4 till there are no more enqueues). So, circular buffer is actually walk the source nodes first then will create circle for enqueued nodes (4).

  
> > +	/* Fence */
> > +	sz += sizeof(RTE_GRAPH_FENCE);
> > +	sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
> > +	graph->nodes_start = sz;
> > +	/* For 0..N node objects with fence */
> > +	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
> > +		sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
> > +		sz += sizeof(struct rte_node);
> > +		/* Pointer to next nodes(edges) */
> > +		sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
> > +	}
> > +
> > +	graph->mem_sz = sz;
> > +	return sz;
> > +}
> > +
> > +static void
> > +graph_header_popluate(struct graph *_graph) {
> > +	struct rte_graph *graph = _graph->graph;
> > +
> > +	graph->tail = 0;
> > +	graph->head = (int32_t)-_graph->src_node_count;
> > +	graph->cir_mask = _graph->cir_mask;
> > +	graph->nb_nodes = _graph->node_count;
> > +	graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
> > +	graph->nodes_start = _graph->nodes_start;
> > +	graph->socket = _graph->socket;
> > +	graph->id = _graph->id;
> > +	memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);
> 
> As I've mentioned above I'm learning the structure of the lib/memory so quick
> question here.  My understanding is that rte_graph is a "view of the 'struct
> graph' sufficient for worker" so does it need both id & name?  Both of them
> seems to be used in error or dump/debug paths.  It probably doesn't matter (e.g.
> for performance) - just asking because 'id' seems to be used only in one place
> (where name could replace it probably).
> 

User will have access to the node info both ways using either name or ID. These are used in slow path. 
It is up to the user how he wants to use it.  


> > +	graph->fence = RTE_GRAPH_FENCE;
> > +}
> > +
> > +static void
> > +graph_nodes_populate(struct graph *_graph) {
> > +	rte_graph_off_t off = _graph->nodes_start;
> > +	struct rte_graph *graph = _graph->graph;
> > +	struct graph_node *graph_node;
> > +	rte_edge_t count, nb_edges;
> > +	const char *parent;
> > +	rte_node_t pid;
> > +
> > +	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
> > +		struct rte_node *node = RTE_PTR_ADD(graph, off);
> > +		memset(node, 0, sizeof(*node));
> > +		node->fence = RTE_GRAPH_FENCE;
> > +		node->off = off;
> > +		node->process = graph_node->node->process;
> > +		memcpy(node->name, graph_node->node->name,
> RTE_GRAPH_NAMESIZE);
> > +		pid = graph_node->node->parent_id;
> > +		if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
> > +			parent = rte_node_id_to_name(pid);
> > +			memcpy(node->parent, parent,
> RTE_GRAPH_NAMESIZE);
> > +		}
> > +		node->id = graph_node->node->id;
> > +		node->parent_id = pid;
> > +		nb_edges = graph_node->node->nb_edges;
> > +		node->nb_edges = nb_edges;
> > +		off += sizeof(struct rte_node);
> > +		/* Copy the name in first pass to replace with rte_node* later*/
> > +		for (count = 0; count < nb_edges; count++)
> > +			node->nodes[count] = (struct rte_node *)&graph_node
> > +						     ->adjacency_list[count]
> > +						     ->node->name[0];
> 
> I'm not sure I understand what is going here.  Please see below ...


See below.

> 
> > +
> > +		off += sizeof(struct rte_node *) * nb_edges;
> > +		off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
> > +		node->next = off;
> > +		__rte_node_stream_alloc(graph, node);
> > +	}
> > +}
> [...]
> > +static int
> > +graph_node_nexts_populate(struct graph *_graph) {
> > +	rte_node_t count, val;
> > +	rte_graph_off_t off;
> > +	struct rte_node *node;
> > +	const struct rte_graph *graph = _graph->graph;
> > +	const char *name;
> > +
> > +	rte_graph_foreach_node(count, off, graph, node) {
> > +		for (val = 0; val < node->nb_edges; val++) {
> > +			name = (const char *)node->nodes[val];
> > +			node->nodes[val] = graph_node_name_to_ptr(graph,
> name);
> 
> ... Is it so that during node the first loop above some node might refer (by name)
> to other node that is not yet "registered" so instead of storing rte_node pointer
> you stored actually pointer to name which you now update to proper rte_node?

Exactly, it is because next nodes are based on name not based on ID. All we need is user has to create all the nodes before graph create. So, that at the time of graph create we will take care of linking the actual nodes based on name.

> 
> > +			if (node->nodes[val] == NULL)
> > +				SET_ERR_JMP(EINVAL, fail, "%s not found",
> name);
> > +		}
> > +	}
> > +
> > +	return 0;
> > +fail:
> > +	return -rte_errno;
> > +}
> [...]
> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 27/29] l3fwd-graph: add graph config and main loop
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 27/29] l3fwd-graph: add graph config and main loop jerinj
@ 2020-04-09 23:04         ` Andrzej Ostruszka
  2020-04-10  9:29           ` Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:04 UTC (permalink / raw)
  To: dev

On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> 
> Add graph creation, configuration logic and graph main loop.
> This graph main loop is run on every slave lcore and calls
> rte_graph_walk() to walk over lcore specific rte_graph.
> Master core accumulates and prints graph walk stats of all the
> lcore's graph's.
> 
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
[...]
> @@ -826,11 +936,26 @@ main(int argc, char **argv)
>  					 "port=%d\n",
>  					 ret, portid);
>  
> +			/* Add this queue node to its graph */
> +			snprintf(qconf->rx_queue_list[queue].node_name,
> +				 RTE_NODE_NAMESIZE, "ethdev_rx-%u-%u", portid,
> +				 queueid);
> +		}
> +
> +		/* Alloc a graph to this lcore only if source exists  */
> +		if (qconf->n_rx_queue) {
> +			qconf->graph_id = nb_graphs;

See below for a comment related to that.

> +			nb_graphs++;
>  		}
>  	}
>  
>  	printf("\n");
>  
> +	/* Ethdev node config, skip rx queue mapping */
> +	ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
> +	if (ret)
> +		rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", ret);
> +
>  	/* Start ports */
>  	RTE_ETH_FOREACH_DEV(portid)
>  	{
> @@ -858,6 +983,119 @@ main(int argc, char **argv)
>  
>  	check_all_ports_link_status(enabled_port_mask);
>  
> +	/* Graph Initialization */
> +	memset(&graph_conf, 0, sizeof(graph_conf));
> +	graph_conf.node_patterns = node_patterns;
> +	nb_patterns = RTE_DIM(node_patterns);
> +
> +	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> +		rte_graph_t graph_id;
> +		rte_edge_t i;
> +
> +		if (rte_lcore_is_enabled(lcore_id) == 0)
> +			continue;
> +
> +		qconf = &lcore_conf[lcore_id];
> +
> +		/* Skip graph creation if no source exists */
> +		if (!qconf->n_rx_queue)
> +			continue;
> +
> +		/* Add rx node patterns of this lcore */
> +		for (i = 0; i < qconf->n_rx_queue; i++) {
> +			graph_conf.node_patterns[nb_patterns + i] =
> +				qconf->rx_queue_list[i].node_name;
> +		}
> +
> +		graph_conf.nb_node_patterns = nb_patterns + i;
> +		graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
> +
> +		snprintf(qconf->name, sizeof(qconf->name), "worker_%u",
> +			 lcore_id);
> +
> +		graph_id = rte_graph_create(qconf->name, &graph_conf);
> +		if (graph_id != qconf->graph_id)
> +			rte_exit(EXIT_FAILURE,
> +				 "rte_graph_create(): graph_id=%d not "
> +				 " as expected for lcore %u(%u\n",
> +				 graph_id, lcore_id, qconf->graph_id);

Should application be checking graph implementation?  Maybe just check
that it is not "invalid" and here store it to qconf->graph_id?

> +
> +		qconf->graph = rte_graph_lookup(qconf->name);
> +		if (!qconf->graph)
> +			rte_exit(EXIT_FAILURE,
> +				 "rte_graph_lookup(): graph %s not found\n",
> +				 qconf->name);
> +	}
> +
> +	memset(&rewrite_data, 0, sizeof(rewrite_data));
> +	rewrite_len = sizeof(rewrite_data);
> +
> +	/* Add route to ip4 graph infra */
> +	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
> +		char route_str[INET6_ADDRSTRLEN * 4];
> +		char abuf[INET6_ADDRSTRLEN];
> +		struct in_addr in;
> +		uint32_t dst_port;
> +		uint16_t next_hop;
> +
> +		/* Skip unused ports */
> +		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
> +		     enabled_port_mask) == 0)
> +			continue;
> +
> +		dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
> +		next_hop = i;

What is the difference between next_hop and i?  It looks to me like they
both have the same type and value.  So maybe instead of another object
just add comment to route_add() below?  No strong feeling here so you
can ignore this comment.

> +
> +		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
> +		snprintf(route_str, sizeof(route_str), "%s / %d (%d)",
> +			 inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
> +			 ipv4_l3fwd_lpm_route_array[i].depth,
> +			 ipv4_l3fwd_lpm_route_array[i].if_out);
> +
> +		ret = rte_node_ip4_route_add(
> +			ipv4_l3fwd_lpm_route_array[i].ip,
> +			ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
> +			RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
> +

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 25/29] l3fwd-graph: add graph based l3fwd skeleton
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
@ 2020-04-09 23:04         ` Andrzej Ostruszka
  2020-04-10  8:23           ` Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:04 UTC (permalink / raw)
  To: dev

On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> 
> Add graph based l3fwd application skeleton with cmdline
> parsing support inline with normal l3fwd.
> 
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
[...]
> +static int
> +parse_config(const char *q_arg)
> +{
> +	enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD };
> +	unsigned long int_fld[_NUM_FLD];
> +	const char *p, *p0 = q_arg;
> +	char *str_fld[_NUM_FLD];
> +	uint32_t size;
> +	char s[256];
> +	char *end;
> +	int i;
> +
> +	nb_lcore_params = 0;
> +
> +	while ((p = strchr(p0, '(')) != NULL) {
> +		++p;
> +		p0 = strchr(p, ')');
> +		if (p0 == NULL)
> +			return -1;
> +
> +		size = p0 - p;
> +		if (size >= sizeof(s))
> +			return -1;
> +
> +		snprintf(s, sizeof(s), "%.*s", size, p);

Could I ask to make this function to be the same as final versions of
l2fwd and l2fwd-event that were recently under review?  There were
couple simple comments there and they apply here also.

> +		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
> +		    _NUM_FLD)
> +			return -1;
> +		for (i = 0; i < _NUM_FLD; i++) {
> +			errno = 0;
> +			int_fld[i] = strtoul(str_fld[i], &end, 0);
> +			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
> +				return -1;
> +		}
> +		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
> +			printf("Exceeded max number of lcore params: %hu\n",
> +			       nb_lcore_params);
> +			return -1;
> +		}
> +		lcore_params_array[nb_lcore_params].port_id =
> +			(uint8_t)int_fld[FLD_PORT];
> +		lcore_params_array[nb_lcore_params].queue_id =
> +			(uint8_t)int_fld[FLD_QUEUE];
> +		lcore_params_array[nb_lcore_params].lcore_id =
> +			(uint8_t)int_fld[FLD_LCORE];
> +		++nb_lcore_params;
> +	}
> +	lcore_params = lcore_params_array;
> +
> +	return 0;
> +}

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 23/29] node: add ipv4 rewrite and lookup ctrl API
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
@ 2020-04-09 23:04         ` Andrzej Ostruszka
  2020-04-10  7:24           ` Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:04 UTC (permalink / raw)
  To: dev

On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> 
> Add ip4_rewrite and ip4_lookup ctrl API. ip4_lookup ctrl
> API is used to add route entries for LPM lookup with
> result data containing next hop id and next proto.
> ip4_rewrite ctrl API is used to add rewrite data for
> every next hop.
> 
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> ---
[...]
> @@ -93,6 +97,18 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
>  
>  		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
>  			 name, id);
> +
> +		/* Prepare the actual name of the cloned node */
> +		snprintf(name, sizeof(name), "ethdev_tx-%u", port_id);
> +
> +		/* Add this tx port node as next to ip4_rewrite_node */
> +		rte_node_edge_update(ip4_rewrite_node->id, RTE_EDGE_ID_INVALID,
> +				     &next_nodes, 1);

Maybe I've missed something but it looks to me like all uses are of
"append" kind.  Either during initialization (with 0) or here, so maybe
a chance to simplify API?

> +		/* Assuming edge id is the last one alloc'ed */
> +		rc = ip4_rewrite_set_next(
> +			port_id, rte_node_edge_count(ip4_rewrite_node->id) - 1);
> +		if (rc < 0)
> +			return rc;
>  	}
>  
>  	ctrl.nb_graphs = nb_graphs;
> diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
> index 3a38f5ad8..d10d17879 100644
> --- a/lib/librte_node/ip4_lookup.c
> +++ b/lib/librte_node/ip4_lookup.c
> @@ -28,6 +28,8 @@ struct ip4_lookup_node_main {
>  	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
>  };
>  
> +static struct ip4_lookup_node_main ip4_lookup_nm;
> +
>  #if defined(RTE_MACHINE_CPUFLAG_NEON)
>  #include "ip4_lookup_neon.h"
>  #elif defined(RTE_ARCH_X86)
> @@ -109,12 +111,90 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
>  
>  #endif
>  
> +int
> +rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
> +		       enum rte_node_ip4_lookup_next next_node)
> +{
> +	char abuf[INET6_ADDRSTRLEN];
> +	struct in_addr in;
> +	uint8_t socket;
> +	uint32_t val;
> +	int ret;
> +
> +	in.s_addr = htonl(ip);
> +	inet_ntop(AF_INET, &in, abuf, sizeof(abuf));
> +	/* Embedded next node id in next hop */
> +	val = (next_node << 16) | next_hop;

I guess this assumes that the next hop is 32 bits.  I might be
misunderstanding the implementation but it looks to me like it is 24bits
(the docs still say "the user data is 1-byte long" though), at least
this is my impression from struct rte_lpm_tbl_entry.

> +	node_dbg("ip4_lookup", "LPM: Adding route %s / %d nh (0x%x)", abuf,
> +		 depth, val);
> +
> +	for (socket = 0; socket < RTE_MAX_NUMA_NODES; socket++) {
> +		if (!ip4_lookup_nm.lpm_tbl[socket])
> +			continue;
> +
> +		ret = rte_lpm_add(ip4_lookup_nm.lpm_tbl[socket], ip, depth,
> +				  val);
> +
> +		if (ret < 0) {
> +			node_err("ip4_lookup",
> +				 "Unable to add entry %s / %d nh (%x) to LPM table on sock %d, rc=%d\n",
> +				 abuf, depth, val, socket, ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}

With regards
Andrzej Ostruszka


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 16/29] node: add ethdev Rx node
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 16/29] node: add ethdev Rx node jerinj
@ 2020-04-09 23:05         ` Andrzej Ostruszka
  2020-04-10  7:00           ` Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:05 UTC (permalink / raw)
  To: dev

On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> 
> Add source rte_node ethdev_rx process function and register
> it. This node is a source node that will be called periodically
> and when called, performs rte_eth_rx_burst() on a specific
> (port, queue) pair and enqueue them as stream of objects to
> next node.
> 
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
[...]
> +/* Callback for soft ptype parsing */
> +static uint16_t
> +eth_pkt_parse_cb(uint16_t port, uint16_t queue, struct rte_mbuf **mbufs,
> +		 uint16_t nb_pkts, uint16_t max_pkts, void *user_param)
> +{
> +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3;
> +	struct rte_ether_hdr *eth_hdr;
> +	uint16_t etype, n_left;
> +	struct rte_mbuf **pkts;
> +
> +	RTE_SET_USED(port);
> +	RTE_SET_USED(queue);
> +	RTE_SET_USED(max_pkts);
> +	RTE_SET_USED(user_param);
> +
> +	pkts = mbufs;
> +	n_left = nb_pkts;
> +	while (n_left >= 12) {
> +
> +		/* Prefetch next-next mbufs */
> +		rte_prefetch0(pkts[8]);
> +		rte_prefetch0(pkts[9]);
> +		rte_prefetch0(pkts[10]);
> +		rte_prefetch0(pkts[11]);
> +
> +		/* Prefetch next mbuf data */
> +		rte_prefetch0(
> +			rte_pktmbuf_mtod(pkts[4], struct rte_ether_hdr *));
> +		rte_prefetch0(
> +			rte_pktmbuf_mtod(pkts[5], struct rte_ether_hdr *));
> +		rte_prefetch0(
> +			rte_pktmbuf_mtod(pkts[6], struct rte_ether_hdr *));
> +		rte_prefetch0(
> +			rte_pktmbuf_mtod(pkts[7], struct rte_ether_hdr *));

I know this is software fallback only (and not likely to be used) but is
this aggressive prefetching always beneficial?  I guess you tested this
on octeon and it works, but if this is supposed to be standard RX node
then maybe this is not always good?

On the other hand if other platforms find that detrimental they can
submit some improvements later :)

> +
> +		mbuf0 = pkts[0];
> +		mbuf1 = pkts[1];
> +		mbuf2 = pkts[2];
> +		mbuf3 = pkts[3];
> +		pkts += 4;
> +		n_left -= 4;
> +
> +		/* Extract ptype of mbuf0 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
> +		etype = eth_hdr->ether_type;
> +		mbuf0->packet_type = l3_ptype(etype, 0);
> +
> +		/* Extract ptype of mbuf1 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
> +		etype = eth_hdr->ether_type;
> +		mbuf1->packet_type = l3_ptype(etype, 0);
> +
> +		/* Extract ptype of mbuf2 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
> +		etype = eth_hdr->ether_type;
> +		mbuf2->packet_type = l3_ptype(etype, 0);
> +
> +		/* Extract ptype of mbuf3 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
> +		etype = eth_hdr->ether_type;
> +		mbuf3->packet_type = l3_ptype(etype, 0);
> +	}
> +
> +	while (n_left > 0) {
> +		mbuf0 = pkts[0];
> +
> +		pkts += 1;
> +		n_left -= 1;
> +
> +		/* Extract ptype of mbuf0 */
> +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
> +		etype = eth_hdr->ether_type;
> +		mbuf0->packet_type = l3_ptype(etype, 0);
> +	}
> +
> +	return nb_pkts;
> +}
[...]

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 12/29] graph: implement fastpath API routines
  2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 12/29] graph: implement fastpath API routines jerinj
@ 2020-04-09 23:07         ` Andrzej Ostruszka
  2020-04-10  9:18           ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:07 UTC (permalink / raw)
  To: dev

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Adding implementation for rte_graph_walk() API. This will perform a walk
> on the circular buffer and call the process function of each node
> and collect the stats if stats collection is enabled.
> 
> Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> ---
[...]
> +__rte_experimental
> +static inline void
> +rte_graph_walk(struct rte_graph *graph)
> +{
> +	const rte_graph_off_t *cir_start = graph->cir_start;
> +	const rte_node_t mask = graph->cir_mask;
> +	uint32_t head = graph->head;
> +	struct rte_node *node;
> +	uint64_t start;
> +	uint16_t rc;
> +	void **objs;
> +
> +	/*
> +	 * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
> +	 * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
> +	 * in a circular buffer fashion.
> +	 *
> +	 *	+-----+ <= cir_start - head [number of source nodes]
> +	 *	|     |
> +	 *	| ... | <= source nodes
> +	 *	|     |
> +	 *	+-----+ <= cir_start [head = 0] [tail = 0]
> +	 *	|     |
> +	 *	| ... | <= pending streams
> +	 *	|     |
> +	 *	+-----+ <= cir_start + mask
> +	 */
> +	while (likely(head != graph->tail)) {
> +		node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
> +		RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
> +		objs = node->objs;
> +		rte_prefetch0(objs);
> +
> +		if (rte_graph_has_stats_feature()) {
> +			start = rte_rdtsc();
> +			rc = node->process(graph, node, objs, node->idx);
> +			node->total_cycles += rte_rdtsc() - start;
> +			node->total_calls++;
> +			node->total_objs += rc;
> +		} else {
> +			node->process(graph, node, objs, node->idx);
> +		}
> +		node->idx = 0;

So I guess this is a responsibility of a node process function to handle
all objects.  What should it do if it is not possible.  E.g. after
tx_burst we usually drop packets, how do you drop objects in graph?  Do
you simply free them (does the node knows how object was allocated?) or
you need to pass it to "null" node.  Process function returns number of
objects processed (e.g. later RX/TX nodes), why it is not used here?

> +		head = likely((int32_t)head > 0) ? head & mask : head;
> +	}
> +	graph->tail = 0;
> +}
[...]
> +__rte_experimental
> +static inline void
> +rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
> +		 rte_edge_t next, void **objs, uint16_t nb_objs)
> +{
> +	node = __rte_node_next_node_get(node, next);
> +	const uint16_t idx = node->idx;
> +
> +	__rte_node_enqueue_prologue(graph, node, idx, nb_objs);
> +
> +	rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
> +	node->idx = idx + nb_objs;
> +}

I see how it works for usual scenario.  But is there some kind of
fork/join operation?  Just trying to imagine scenario where I have a
bunch of co-processors doing different operations for given
object/packet.  Then to to minimize latency I'd like to use them in
parallel and when all done then enqueue it to next node.  Is there a
support for that in terms of graph lib or should that be handled in the
process function of a single node?

[...]
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Enqueue objs to multiple next nodes for further processing and
> + * set the next nodes to pending state in the circular buffer.
> + * objs[i] will be enqueued to nexts[i].
> + *
> + * @param graph
> + *   Graph pointer returned from rte_graph_lookup().
> + * @param node
> + *   Current node pointer.
> + * @param nexts
> + *   List of relative next node indices to enqueue objs.
> + * @param objs
> + *   List of objs to enqueue.
> + * @param nb_objs
> + *   Number of objs to enqueue.
> + */
> +__rte_experimental
> +static inline void
> +rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
> +		      rte_edge_t *nexts, void **objs, uint16_t nb_objs)
> +{
> +	uint16_t i;
> +
> +	for (i = 0; i < nb_objs; i++)
> +		rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);

I have noticed comments about x1/2/4 functions but since you defended
the performance there why not having some kind of use of them here
(Duff's device like unrolling)?  Just like you have in your performance
test.

[...]
> +__rte_experimental
> +static inline void **
> +rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
> +			 rte_edge_t next, uint16_t nb_objs)
> +{
> +	node = __rte_node_next_node_get(node, next);
> +	const uint16_t idx = node->idx;
> +	uint16_t free_space = node->size - idx;
> +
> +	if (unlikely(free_space < nb_objs))
> +		__rte_node_stream_alloc_size(graph, node, nb_objs);
> +
> +	return &node->objs[idx];
> +}
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Put the next stream to pending state in the circular buffer
> + * for further processing. Should be invoked followed by
> + * rte_node_next_stream_get().

Is the last sentence correct?

> + *
> + * @param graph
> + *   Graph pointer returned from rte_graph_lookup().
> + * @param node
> + *   Current node pointer.
> + * @param next
> + *   Relative next node index..
> + * @param idx
> + *   Number of objs updated in the stream after getting the stream using
> + *   rte_node_next_stream_get.
> + *
> + * @see rte_node_next_stream_get().
> + */
> +__rte_experimental
> +static inline void
> +rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,
> +			 rte_edge_t next, uint16_t idx)

I understood the stream_move as an optimized enqueue, but could you
describe how these _get/put are meant to be used, and why not use
stream_move/enqueue?  I see in later (testing) patches that it is used
for source nodes, and I'm not sure I understand the difference between them.

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
@ 2020-04-09 23:07         ` Andrzej Ostruszka
  2020-04-10  5:09           ` Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:07 UTC (permalink / raw)
  To: dev

On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> 
> Add ctrl api to setup ethdev_rx and ethdev_tx node.
> This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
> nodes with specific (port, queue) pairs updated in their context.
> All the ethdev ports and queues are setup before this api
> is called.
> 
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> ---
[...]
> +struct rte_node_mbuf_priv1 {
> +	union {
> +		/* IP4 rewrite */
> +		struct {
> +			uint16_t nh;
> +			uint16_t ttl;
> +			uint32_t cksum;
> +		};
> +
> +		uint64_t u;
> +	};
> +};
> +
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Node mbuf private data to store crypto operation.
> + */
> +struct rte_node_mbuf_priv2 {
> +	union {
> +		/* Sym crypto */
> +		struct {
> +			struct rte_crypto_op op;
> +		};
> +	};
> +} __rte_cache_aligned;

Why such definition?

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node jerinj
@ 2020-04-09 23:07         ` Andrzej Ostruszka
  2020-04-10 10:20           ` Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:07 UTC (permalink / raw)
  To: dev

On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> From: Pavan Nikhilesh <pbhagavatula@marvell.com>
> 
> Add IPv4 lookup process function for ip4_lookup node.
> This node performs LPM lookup using simple RTE_LPM API on every packet
> received and forwards it to a next node that is identified by lookup
> result.
> 
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
[...]
> +static uint16_t
> +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> +			void **objs, uint16_t nb_objs)
> +{
> +	struct rte_ipv4_hdr *ipv4_hdr;
> +	void **to_next, **from;
> +	uint16_t last_spec = 0;
> +	struct rte_mbuf *mbuf;
> +	rte_edge_t next_index;
> +	struct rte_lpm *lpm;
> +	uint16_t held = 0;
> +	uint32_t drop_nh;
> +	int i, rc;
> +
> +	/* Speculative next */
> +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
> +	/* Drop node */
> +	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
> +
> +	/* Get socket specific LPM from ctx */
> +	lpm = *((struct rte_lpm **)node->ctx);
> +	from = objs;
> +
> +	/* Get stream for the speculated next node */
> +	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
> +	for (i = 0; i < nb_objs; i++) {
> +		uint32_t next_hop;
> +		uint16_t next;
> +
> +		mbuf = (struct rte_mbuf *)objs[i];
> +
> +		/* Extract DIP of mbuf0 */
> +		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
> +				sizeof(struct rte_ether_hdr));
> +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> +		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
> +		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
> +
> +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
> +				    &next_hop);
> +		next_hop = (rc == 0) ? next_hop : drop_nh;

Maybe simple if here?  I see the same in other patches.

> +
> +		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
> +		next_hop = next_hop >> 16;
> +		next = (uint16_t)next_hop;
> +
> +		if (unlikely(next_index != next)) {
> +			/* Copy things successfully speculated till now */
> +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> +			from += last_spec;
> +			to_next += last_spec;
> +			held += last_spec;
> +			last_spec = 0;
> +
> +			rte_node_enqueue_x1(graph, node, next, from[0]);
> +			from += 1;
> +		} else {
> +			last_spec += 1;
> +		}
> +	}
> +
> +	/* !!! Home run !!! */
> +	if (likely(last_spec == nb_objs)) {
> +		rte_node_next_stream_move(graph, node, next_index);
> +		return nb_objs;
> +	}
> +	held += last_spec;
> +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> +	rte_node_next_stream_put(graph, node, next_index, held);

OK.  Forget my comments in different mail about difference between
encode/put - I got it now.

> +
> +	return nb_objs;
> +}
> +
[...]

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (28 preceding siblings ...)
  2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 29/29] doc: add l3fwd graph application user guide jerinj
@ 2020-04-09 23:13       ` Andrzej Ostruszka
  2020-04-10  9:07         ` Jerin Jacob
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
  30 siblings, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-09 23:13 UTC (permalink / raw)
  To: Jerin Jacob Kollanukkaran, dev

On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Using graph traversal for packet processing is a proven architecture
> that has been implemented in various open source libraries.
> 
> Graph architecture for packet processing enables abstracting the data
> processing functions as “nodes” and “links” them together to create a
> complex “graph” to create reusable/modular data processing functions. 
> 
> The patchset further includes performance enhancements and modularity
> to the DPDK as discussed in more detail below.
[...]

Jerin et al.

I have finished the review and have no more comments ... apart from that
I like it :).

With regards
Andrzej Ostruszka


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-04-09 23:07         ` Andrzej Ostruszka
@ 2020-04-10  5:09           ` Nithin Dabilpuram
  2020-04-10  8:22             ` Nithin Dabilpuram
  2020-04-10 12:52             ` Andrzej Ostruszka
  0 siblings, 2 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10  5:09 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 01:07:17AM +0200, Andrzej Ostruszka wrote:
> On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > 
> > Add ctrl api to setup ethdev_rx and ethdev_tx node.
> > This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
> > nodes with specific (port, queue) pairs updated in their context.
> > All the ethdev ports and queues are setup before this api
> > is called.
> > 
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> > ---
> [...]
> > +struct rte_node_mbuf_priv1 {
> > +	union {
> > +		/* IP4 rewrite */
> > +		struct {
> > +			uint16_t nh;
> > +			uint16_t ttl;
> > +			uint32_t cksum;
> > +		};
> > +
> > +		uint64_t u;
> > +	};
> > +};
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Node mbuf private data to store crypto operation.
> > + */
> > +struct rte_node_mbuf_priv2 {
> > +	union {
> > +		/* Sym crypto */
> > +		struct {
> > +			struct rte_crypto_op op;
> > +		};
> > +	};
> > +} __rte_cache_aligned;
> 
> Why such definition?

For communication b/w nodes, we need some per mbuf private space.
We defined it into two halfs for performance reasons as
#1 rte_node_mbuf_priv1(8 bytes) mapped to mbuf->udata64
#2 rte_node_mbuf_priv2(RTE_CACHE_LINE_SIZE bytes) mapped to mbuf private area.

#1 is smaller area and will not have a cache miss when accessed as mbuf
is already in cache.
#2 is larger area and probably good enough for many use cases like ipsec, crypto 
etc, and there will be an extra cost of cache miss to access it.

Atleast in OCTEONTX2, we are able to see 27% performance drop, if use single
private area #2 for everything instead.

Since pkt_mbuf pool are created by application, we these structures are defined
here have a check in ctrl api if the pkt_mbuf pool meets the mbuf private area
size requirement.

> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 16/29] node: add ethdev Rx node
  2020-04-09 23:05         ` Andrzej Ostruszka
@ 2020-04-10  7:00           ` Nithin Dabilpuram
  0 siblings, 0 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10  7:00 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 01:05:08AM +0200, Andrzej Ostruszka wrote:
> On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > 
> > Add source rte_node ethdev_rx process function and register
> > it. This node is a source node that will be called periodically
> > and when called, performs rte_eth_rx_burst() on a specific
> > (port, queue) pair and enqueue them as stream of objects to
> > next node.
> > 
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> [...]
> > +/* Callback for soft ptype parsing */
> > +static uint16_t
> > +eth_pkt_parse_cb(uint16_t port, uint16_t queue, struct rte_mbuf **mbufs,
> > +		 uint16_t nb_pkts, uint16_t max_pkts, void *user_param)
> > +{
> > +	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3;
> > +	struct rte_ether_hdr *eth_hdr;
> > +	uint16_t etype, n_left;
> > +	struct rte_mbuf **pkts;
> > +
> > +	RTE_SET_USED(port);
> > +	RTE_SET_USED(queue);
> > +	RTE_SET_USED(max_pkts);
> > +	RTE_SET_USED(user_param);
> > +
> > +	pkts = mbufs;
> > +	n_left = nb_pkts;
> > +	while (n_left >= 12) {
> > +
> > +		/* Prefetch next-next mbufs */
> > +		rte_prefetch0(pkts[8]);
> > +		rte_prefetch0(pkts[9]);
> > +		rte_prefetch0(pkts[10]);
> > +		rte_prefetch0(pkts[11]);
> > +
> > +		/* Prefetch next mbuf data */
> > +		rte_prefetch0(
> > +			rte_pktmbuf_mtod(pkts[4], struct rte_ether_hdr *));
> > +		rte_prefetch0(
> > +			rte_pktmbuf_mtod(pkts[5], struct rte_ether_hdr *));
> > +		rte_prefetch0(
> > +			rte_pktmbuf_mtod(pkts[6], struct rte_ether_hdr *));
> > +		rte_prefetch0(
> > +			rte_pktmbuf_mtod(pkts[7], struct rte_ether_hdr *));
> 
> I know this is software fallback only (and not likely to be used) but is
> this aggressive prefetching always beneficial?  I guess you tested this
> on octeon and it works, but if this is supposed to be standard RX node
> then maybe this is not always good?
> 
> On the other hand if other platforms find that detrimental they can
> submit some improvements later :)

I tested it now in octeon and there is 6% increasing when using prefetch based while loop
instead of non-prefetch based while loop.

Yes, it is not intended to be used normally, but I just followed ideology of prefetch ahead 
before use. It could be changed later if needed for other platforms or split into platform 
dependent implementation like lookup node.

> 
> > +
> > +		mbuf0 = pkts[0];
> > +		mbuf1 = pkts[1];
> > +		mbuf2 = pkts[2];
> > +		mbuf3 = pkts[3];
> > +		pkts += 4;
> > +		n_left -= 4;
> > +
> > +		/* Extract ptype of mbuf0 */
> > +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
> > +		etype = eth_hdr->ether_type;
> > +		mbuf0->packet_type = l3_ptype(etype, 0);
> > +
> > +		/* Extract ptype of mbuf1 */
> > +		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
> > +		etype = eth_hdr->ether_type;
> > +		mbuf1->packet_type = l3_ptype(etype, 0);
> > +
> > +		/* Extract ptype of mbuf2 */
> > +		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
> > +		etype = eth_hdr->ether_type;
> > +		mbuf2->packet_type = l3_ptype(etype, 0);
> > +
> > +		/* Extract ptype of mbuf3 */
> > +		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
> > +		etype = eth_hdr->ether_type;
> > +		mbuf3->packet_type = l3_ptype(etype, 0);
> > +	}
> > +
> > +	while (n_left > 0) {
> > +		mbuf0 = pkts[0];
> > +
> > +		pkts += 1;
> > +		n_left -= 1;
> > +
> > +		/* Extract ptype of mbuf0 */
> > +		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
> > +		etype = eth_hdr->ether_type;
> > +		mbuf0->packet_type = l3_ptype(etype, 0);
> > +	}
> > +
> > +	return nb_pkts;
> > +}
> [...]
> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 23/29] node: add ipv4 rewrite and lookup ctrl API
  2020-04-09 23:04         ` Andrzej Ostruszka
@ 2020-04-10  7:24           ` Nithin Dabilpuram
  0 siblings, 0 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10  7:24 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 01:04:37AM +0200, Andrzej Ostruszka wrote:
> On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > 
> > Add ip4_rewrite and ip4_lookup ctrl API. ip4_lookup ctrl
> > API is used to add route entries for LPM lookup with
> > result data containing next hop id and next proto.
> > ip4_rewrite ctrl API is used to add rewrite data for
> > every next hop.
> > 
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> > ---
> [...]
> > @@ -93,6 +97,18 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
> >  
> >  		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
> >  			 name, id);
> > +
> > +		/* Prepare the actual name of the cloned node */
> > +		snprintf(name, sizeof(name), "ethdev_tx-%u", port_id);
> > +
> > +		/* Add this tx port node as next to ip4_rewrite_node */
> > +		rte_node_edge_update(ip4_rewrite_node->id, RTE_EDGE_ID_INVALID,
> > +				     &next_nodes, 1);
> 
> Maybe I've missed something but it looks to me like all uses are of
> "append" kind.  Either during initialization (with 0) or here, so maybe
> a chance to simplify API?
> 
> > +		/* Assuming edge id is the last one alloc'ed */
> > +		rc = ip4_rewrite_set_next(
> > +			port_id, rte_node_edge_count(ip4_rewrite_node->id) - 1);
> > +		if (rc < 0)
> > +			return rc;
> >  	}
> >  
> >  	ctrl.nb_graphs = nb_graphs;
> > diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
> > index 3a38f5ad8..d10d17879 100644
> > --- a/lib/librte_node/ip4_lookup.c
> > +++ b/lib/librte_node/ip4_lookup.c
> > @@ -28,6 +28,8 @@ struct ip4_lookup_node_main {
> >  	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
> >  };
> >  
> > +static struct ip4_lookup_node_main ip4_lookup_nm;
> > +
> >  #if defined(RTE_MACHINE_CPUFLAG_NEON)
> >  #include "ip4_lookup_neon.h"
> >  #elif defined(RTE_ARCH_X86)
> > @@ -109,12 +111,90 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> >  
> >  #endif
> >  
> > +int
> > +rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
> > +		       enum rte_node_ip4_lookup_next next_node)
> > +{
> > +	char abuf[INET6_ADDRSTRLEN];
> > +	struct in_addr in;
> > +	uint8_t socket;
> > +	uint32_t val;
> > +	int ret;
> > +
> > +	in.s_addr = htonl(ip);
> > +	inet_ntop(AF_INET, &in, abuf, sizeof(abuf));
> > +	/* Embedded next node id in next hop */
> > +	val = (next_node << 16) | next_hop;
> 
> I guess this assumes that the next hop is 32 bits.  I might be
> misunderstanding the implementation but it looks to me like it is 24bits
> (the docs still say "the user data is 1-byte long" though), at least
> this is my impression from struct rte_lpm_tbl_entry.

Agree. I'll mask out upper 8 bits here in next patch. 
Since next_node is enum and is in control of lookup node implementation, 
BIT(23:16) for "next_node" should be sufficient with space for 256 next nodes
for now.

> 
> > +	node_dbg("ip4_lookup", "LPM: Adding route %s / %d nh (0x%x)", abuf,
> > +		 depth, val);
> > +
> > +	for (socket = 0; socket < RTE_MAX_NUMA_NODES; socket++) {
> > +		if (!ip4_lookup_nm.lpm_tbl[socket])
> > +			continue;
> > +
> > +		ret = rte_lpm_add(ip4_lookup_nm.lpm_tbl[socket], ip, depth,
> > +				  val);
> > +
> > +		if (ret < 0) {
> > +			node_err("ip4_lookup",
> > +				 "Unable to add entry %s / %d nh (%x) to LPM table on sock %d, rc=%d\n",
> > +				 abuf, depth, val, socket, ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> 
> With regards
> Andrzej Ostruszka
> 

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-04-10  5:09           ` Nithin Dabilpuram
@ 2020-04-10  8:22             ` Nithin Dabilpuram
  2020-04-10 12:52             ` Andrzej Ostruszka
  1 sibling, 0 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10  8:22 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 10:39:44AM +0530, Nithin Dabilpuram wrote:
> On Fri, Apr 10, 2020 at 01:07:17AM +0200, Andrzej Ostruszka wrote:
> > On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > > From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > > 
> > > Add ctrl api to setup ethdev_rx and ethdev_tx node.
> > > This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
> > > nodes with specific (port, queue) pairs updated in their context.
> > > All the ethdev ports and queues are setup before this api
> > > is called.
> > > 
> > > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> > > ---
> > [...]
> > > +struct rte_node_mbuf_priv1 {
> > > +	union {
> > > +		/* IP4 rewrite */
> > > +		struct {
> > > +			uint16_t nh;
> > > +			uint16_t ttl;
> > > +			uint32_t cksum;
> > > +		};
> > > +
> > > +		uint64_t u;
> > > +	};
> > > +};
> > > +
> > > +/**
> > > + * @warning
> > > + * @b EXPERIMENTAL: this API may change without prior notice
> > > + *
> > > + * Node mbuf private data to store crypto operation.
> > > + */
> > > +struct rte_node_mbuf_priv2 {
> > > +	union {
> > > +		/* Sym crypto */
> > > +		struct {
> > > +			struct rte_crypto_op op;
> > > +		};
> > > +	};
> > > +} __rte_cache_aligned;
> > 
> > Why such definition?
> 
> For communication b/w nodes, we need some per mbuf private space.
> We defined it into two halfs for performance reasons as
> #1 rte_node_mbuf_priv1(8 bytes) mapped to mbuf->udata64
> #2 rte_node_mbuf_priv2(RTE_CACHE_LINE_SIZE bytes) mapped to mbuf private area.
> 
> #1 is smaller area and will not have a cache miss when accessed as mbuf
> is already in cache.
> #2 is larger area and probably good enough for many use cases like ipsec, crypto 
> etc, and there will be an extra cost of cache miss to access it.
> 
> Atleast in OCTEONTX2, we are able to see 27% performance drop, if use single
> private area #2 for everything instead.
> 
> Since pkt_mbuf pool are created by application, we these structures are defined
> here have a check in ctrl api if the pkt_mbuf pool meets the mbuf private area
> size requirement.

Just wanted to update that I'll also rename this structure and 
related api's to not start with "rte_" in next version
as they are not visible outside this librte_node library.


> 
> > 
> > With regards
> > Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 25/29] l3fwd-graph: add graph based l3fwd skeleton
  2020-04-09 23:04         ` Andrzej Ostruszka
@ 2020-04-10  8:23           ` Nithin Dabilpuram
  0 siblings, 0 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10  8:23 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 01:04:20AM +0200, Andrzej Ostruszka wrote:
> On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > 
> > Add graph based l3fwd application skeleton with cmdline
> > parsing support inline with normal l3fwd.
> > 
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> [...]
> > +static int
> > +parse_config(const char *q_arg)
> > +{
> > +	enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD };
> > +	unsigned long int_fld[_NUM_FLD];
> > +	const char *p, *p0 = q_arg;
> > +	char *str_fld[_NUM_FLD];
> > +	uint32_t size;
> > +	char s[256];
> > +	char *end;
> > +	int i;
> > +
> > +	nb_lcore_params = 0;
> > +
> > +	while ((p = strchr(p0, '(')) != NULL) {
> > +		++p;
> > +		p0 = strchr(p, ')');
> > +		if (p0 == NULL)
> > +			return -1;
> > +
> > +		size = p0 - p;
> > +		if (size >= sizeof(s))
> > +			return -1;
> > +
> > +		snprintf(s, sizeof(s), "%.*s", size, p);
> 
> Could I ask to make this function to be the same as final versions of
> l2fwd and l2fwd-event that were recently under review?  There were
> couple simple comments there and they apply here also.

Ok. I'll sync up fixes from l2fwd-event to this function in v5.
> 
> > +		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
> > +		    _NUM_FLD)
> > +			return -1;
> > +		for (i = 0; i < _NUM_FLD; i++) {
> > +			errno = 0;
> > +			int_fld[i] = strtoul(str_fld[i], &end, 0);
> > +			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
> > +				return -1;
> > +		}
> > +		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
> > +			printf("Exceeded max number of lcore params: %hu\n",
> > +			       nb_lcore_params);
> > +			return -1;
> > +		}
> > +		lcore_params_array[nb_lcore_params].port_id =
> > +			(uint8_t)int_fld[FLD_PORT];
> > +		lcore_params_array[nb_lcore_params].queue_id =
> > +			(uint8_t)int_fld[FLD_QUEUE];
> > +		lcore_params_array[nb_lcore_params].lcore_id =
> > +			(uint8_t)int_fld[FLD_LCORE];
> > +		++nb_lcore_params;
> > +	}
> > +	lcore_params = lcore_params_array;
> > +
> > +	return 0;
> > +}
> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem
  2020-04-09 23:13       ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem Andrzej Ostruszka
@ 2020-04-10  9:07         ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-10  9:07 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: Jerin Jacob Kollanukkaran, dev

On Fri, Apr 10, 2020 at 4:43 AM Andrzej Ostruszka <amo@semihalf.com> wrote:
>
> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> > From: Jerin Jacob <jerinj@marvell.com>
> >
> > Using graph traversal for packet processing is a proven architecture
> > that has been implemented in various open source libraries.
> >
> > Graph architecture for packet processing enables abstracting the data
> > processing functions as “nodes” and “links” them together to create a
> > complex “graph” to create reusable/modular data processing functions.
> >
> > The patchset further includes performance enhancements and modularity
> > to the DPDK as discussed in more detail below.
> [...]
>
> Jerin et al.
>
> I have finished the review and have no more comments ... apart from that
> I like it :).

Thanks for the review. I will send the v5 after addressing all the comments.


>
> With regards
> Andrzej Ostruszka
>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 12/29] graph: implement fastpath API routines
  2020-04-09 23:07         ` Andrzej Ostruszka
@ 2020-04-10  9:18           ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-10  9:18 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dpdk-dev

On Fri, Apr 10, 2020 at 4:37 AM Andrzej Ostruszka <amo@semihalf.com> wrote:
>
> On 4/5/20 10:55 AM, jerinj@marvell.com wrote:
> > From: Jerin Jacob <jerinj@marvell.com>
> >
> > Adding implementation for rte_graph_walk() API. This will perform a walk
> > on the circular buffer and call the process function of each node
> > and collect the stats if stats collection is enabled.
> >
> > Signed-off-by: Jerin Jacob <jerinj@marvell.com>
> > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > ---
> [...]
> > +__rte_experimental
> > +static inline void
> > +rte_graph_walk(struct rte_graph *graph)
> > +{
> > +     const rte_graph_off_t *cir_start = graph->cir_start;
> > +     const rte_node_t mask = graph->cir_mask;
> > +     uint32_t head = graph->head;
> > +     struct rte_node *node;
> > +     uint64_t start;
> > +     uint16_t rc;
> > +     void **objs;
> > +
> > +     /*
> > +      * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
> > +      * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
> > +      * in a circular buffer fashion.
> > +      *
> > +      *      +-----+ <= cir_start - head [number of source nodes]
> > +      *      |     |
> > +      *      | ... | <= source nodes
> > +      *      |     |
> > +      *      +-----+ <= cir_start [head = 0] [tail = 0]
> > +      *      |     |
> > +      *      | ... | <= pending streams
> > +      *      |     |
> > +      *      +-----+ <= cir_start + mask
> > +      */
> > +     while (likely(head != graph->tail)) {
> > +             node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
> > +             RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
> > +             objs = node->objs;
> > +             rte_prefetch0(objs);
> > +
> > +             if (rte_graph_has_stats_feature()) {
> > +                     start = rte_rdtsc();
> > +                     rc = node->process(graph, node, objs, node->idx);
> > +                     node->total_cycles += rte_rdtsc() - start;
> > +                     node->total_calls++;
> > +                     node->total_objs += rc;
> > +             } else {
> > +                     node->process(graph, node, objs, node->idx);
> > +             }
> > +             node->idx = 0;
>
> So I guess this is a responsibility of a node process function to handle
> all objects.  What should it do if it is not possible.  E.g. after
> tx_burst we usually drop packets, how do you drop objects in graph?  Do
> you simply free them (does the node knows how object was allocated?) or
> you need to pass it to "null" node.  Process function returns number of
> objects processed (e.g. later RX/TX nodes), why it is not used here?

Graph library deals with (void *)objects, not the mbuf. So it can be used with
any datatypes.
For packet drop requirements, a "packet drop" node added in the libnode to
free the packets to mempool.
The downstream node can send  to "null"  node  if it needs to drop on the floor.
So it is the downstream node decision. The graph library is not
dictating any domain-specific
details.

>
> > +             head = likely((int32_t)head > 0) ? head & mask : head;
> > +     }
> > +     graph->tail = 0;
> > +}
> [...]
> > +__rte_experimental
> > +static inline void
> > +rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
> > +              rte_edge_t next, void **objs, uint16_t nb_objs)
> > +{
> > +     node = __rte_node_next_node_get(node, next);
> > +     const uint16_t idx = node->idx;
> > +
> > +     __rte_node_enqueue_prologue(graph, node, idx, nb_objs);
> > +
> > +     rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
> > +     node->idx = idx + nb_objs;
> > +}
>
> I see how it works for usual scenario.  But is there some kind of
> fork/join operation?  Just trying to imagine scenario where I have a
> bunch of co-processors doing different operations for given
> object/packet.  Then to to minimize latency I'd like to use them in
> parallel and when all done then enqueue it to next node.  Is there a
> support for that in terms of graph lib or should that be handled in the
> process function of a single node?

Yes. We  can create multiple graphs(each attached to a core) which
can have multiple instances of SRC nodes.

>
> [...]
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Enqueue objs to multiple next nodes for further processing and
> > + * set the next nodes to pending state in the circular buffer.
> > + * objs[i] will be enqueued to nexts[i].
> > + *
> > + * @param graph
> > + *   Graph pointer returned from rte_graph_lookup().
> > + * @param node
> > + *   Current node pointer.
> > + * @param nexts
> > + *   List of relative next node indices to enqueue objs.
> > + * @param objs
> > + *   List of objs to enqueue.
> > + * @param nb_objs
> > + *   Number of objs to enqueue.
> > + */
> > +__rte_experimental
> > +static inline void
> > +rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
> > +                   rte_edge_t *nexts, void **objs, uint16_t nb_objs)
> > +{
> > +     uint16_t i;
> > +
> > +     for (i = 0; i < nb_objs; i++)
> > +             rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);
>
> I have noticed comments about x1/2/4 functions but since you defended
> the performance there why not having some kind of use of them here
> (Duff's device like unrolling)?  Just like you have in your performance
> test.

This function will be replaced with more SIMD type processing in future
based on the performance need.

>
> [...]
> > +__rte_experimental
> > +static inline void **
> > +rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
> > +                      rte_edge_t next, uint16_t nb_objs)
> > +{
> > +     node = __rte_node_next_node_get(node, next);
> > +     const uint16_t idx = node->idx;
> > +     uint16_t free_space = node->size - idx;
> > +
> > +     if (unlikely(free_space < nb_objs))
> > +             __rte_node_stream_alloc_size(graph, node, nb_objs);
> > +
> > +     return &node->objs[idx];
> > +}
> > +
> > +/**
> > + * @warning
> > + * @b EXPERIMENTAL: this API may change without prior notice
> > + *
> > + * Put the next stream to pending state in the circular buffer
> > + * for further processing. Should be invoked followed by
> > + * rte_node_next_stream_get().
>
> Is the last sentence correct?

I will reword to "Should be invoked after rte_node_next_stream_get()"

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 27/29] l3fwd-graph: add graph config and main loop
  2020-04-09 23:04         ` Andrzej Ostruszka
@ 2020-04-10  9:29           ` Nithin Dabilpuram
  0 siblings, 0 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10  9:29 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 01:04:00AM +0200, Andrzej Ostruszka wrote:
> On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > 
> > Add graph creation, configuration logic and graph main loop.
> > This graph main loop is run on every slave lcore and calls
> > rte_graph_walk() to walk over lcore specific rte_graph.
> > Master core accumulates and prints graph walk stats of all the
> > lcore's graph's.
> > 
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > ---
> [...]
> > @@ -826,11 +936,26 @@ main(int argc, char **argv)
> >  					 "port=%d\n",
> >  					 ret, portid);
> >  
> > +			/* Add this queue node to its graph */
> > +			snprintf(qconf->rx_queue_list[queue].node_name,
> > +				 RTE_NODE_NAMESIZE, "ethdev_rx-%u-%u", portid,
> > +				 queueid);
> > +		}
> > +
> > +		/* Alloc a graph to this lcore only if source exists  */
> > +		if (qconf->n_rx_queue) {
> > +			qconf->graph_id = nb_graphs;
> 
> See below for a comment related to that.
> 
> > +			nb_graphs++;
> >  		}
> >  	}
> >  
> >  	printf("\n");
> >  
> > +	/* Ethdev node config, skip rx queue mapping */
> > +	ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
> > +	if (ret)
> > +		rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", ret);
> > +
> >  	/* Start ports */
> >  	RTE_ETH_FOREACH_DEV(portid)
> >  	{
> > @@ -858,6 +983,119 @@ main(int argc, char **argv)
> >  
> >  	check_all_ports_link_status(enabled_port_mask);
> >  
> > +	/* Graph Initialization */
> > +	memset(&graph_conf, 0, sizeof(graph_conf));
> > +	graph_conf.node_patterns = node_patterns;
> > +	nb_patterns = RTE_DIM(node_patterns);
> > +
> > +	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> > +		rte_graph_t graph_id;
> > +		rte_edge_t i;
> > +
> > +		if (rte_lcore_is_enabled(lcore_id) == 0)
> > +			continue;
> > +
> > +		qconf = &lcore_conf[lcore_id];
> > +
> > +		/* Skip graph creation if no source exists */
> > +		if (!qconf->n_rx_queue)
> > +			continue;
> > +
> > +		/* Add rx node patterns of this lcore */
> > +		for (i = 0; i < qconf->n_rx_queue; i++) {
> > +			graph_conf.node_patterns[nb_patterns + i] =
> > +				qconf->rx_queue_list[i].node_name;
> > +		}
> > +
> > +		graph_conf.nb_node_patterns = nb_patterns + i;
> > +		graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
> > +
> > +		snprintf(qconf->name, sizeof(qconf->name), "worker_%u",
> > +			 lcore_id);
> > +
> > +		graph_id = rte_graph_create(qconf->name, &graph_conf);
> > +		if (graph_id != qconf->graph_id)
> > +			rte_exit(EXIT_FAILURE,
> > +				 "rte_graph_create(): graph_id=%d not "
> > +				 " as expected for lcore %u(%u\n",
> > +				 graph_id, lcore_id, qconf->graph_id);
> 
> Should application be checking graph implementation?  Maybe just check
> that it is not "invalid" and here store it to qconf->graph_id?

Ack, will fix it in V5.
> 
> > +
> > +		qconf->graph = rte_graph_lookup(qconf->name);
> > +		if (!qconf->graph)
> > +			rte_exit(EXIT_FAILURE,
> > +				 "rte_graph_lookup(): graph %s not found\n",
> > +				 qconf->name);
> > +	}
> > +
> > +	memset(&rewrite_data, 0, sizeof(rewrite_data));
> > +	rewrite_len = sizeof(rewrite_data);
> > +
> > +	/* Add route to ip4 graph infra */
> > +	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
> > +		char route_str[INET6_ADDRSTRLEN * 4];
> > +		char abuf[INET6_ADDRSTRLEN];
> > +		struct in_addr in;
> > +		uint32_t dst_port;
> > +		uint16_t next_hop;
> > +
> > +		/* Skip unused ports */
> > +		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
> > +		     enabled_port_mask) == 0)
> > +			continue;
> > +
> > +		dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
> > +		next_hop = i;
> 
> What is the difference between next_hop and i?  It looks to me like they
> both have the same type and value.  So maybe instead of another object
> just add comment to route_add() below?  No strong feeling here so you
> can ignore this comment.

Here there is no difference, but just wanted to say that next_hop is a
user choosen identifier to add rewrite data and use the same id to link
a route added to that next hop. There could be multiple routes pointing
to same next hop(gateway) in reality.
I'll change that to 'i' and add comment describing it in V5.
> 
> > +
> > +		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
> > +		snprintf(route_str, sizeof(route_str), "%s / %d (%d)",
> > +			 inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
> > +			 ipv4_l3fwd_lpm_route_array[i].depth,
> > +			 ipv4_l3fwd_lpm_route_array[i].if_out);
> > +
> > +		ret = rte_node_ip4_route_add(
> > +			ipv4_l3fwd_lpm_route_array[i].ip,
> > +			ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
> > +			RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
> > +
> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node
  2020-04-09 23:07         ` Andrzej Ostruszka
@ 2020-04-10 10:20           ` Nithin Dabilpuram
  2020-04-10 14:41             ` Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10 10:20 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 01:07:34AM +0200, Andrzej Ostruszka wrote:
> On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > From: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > 
> > Add IPv4 lookup process function for ip4_lookup node.
> > This node performs LPM lookup using simple RTE_LPM API on every packet
> > received and forwards it to a next node that is identified by lookup
> > result.
> > 
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> [...]
> > +static uint16_t
> > +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> > +			void **objs, uint16_t nb_objs)
> > +{
> > +	struct rte_ipv4_hdr *ipv4_hdr;
> > +	void **to_next, **from;
> > +	uint16_t last_spec = 0;
> > +	struct rte_mbuf *mbuf;
> > +	rte_edge_t next_index;
> > +	struct rte_lpm *lpm;
> > +	uint16_t held = 0;
> > +	uint32_t drop_nh;
> > +	int i, rc;
> > +
> > +	/* Speculative next */
> > +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
> > +	/* Drop node */
> > +	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
> > +
> > +	/* Get socket specific LPM from ctx */
> > +	lpm = *((struct rte_lpm **)node->ctx);
> > +	from = objs;
> > +
> > +	/* Get stream for the speculated next node */
> > +	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
> > +	for (i = 0; i < nb_objs; i++) {
> > +		uint32_t next_hop;
> > +		uint16_t next;
> > +
> > +		mbuf = (struct rte_mbuf *)objs[i];
> > +
> > +		/* Extract DIP of mbuf0 */
> > +		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
> > +				sizeof(struct rte_ether_hdr));
> > +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> > +		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
> > +		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
> > +
> > +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
> > +				    &next_hop);
> > +		next_hop = (rc == 0) ? next_hop : drop_nh;
> 
> Maybe simple if here?  I see the same in other patches.

Will fix it in V5.
> 
> > +
> > +		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
> > +		next_hop = next_hop >> 16;
> > +		next = (uint16_t)next_hop;
> > +
> > +		if (unlikely(next_index != next)) {
> > +			/* Copy things successfully speculated till now */
> > +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > +			from += last_spec;
> > +			to_next += last_spec;
> > +			held += last_spec;
> > +			last_spec = 0;
> > +
> > +			rte_node_enqueue_x1(graph, node, next, from[0]);
> > +			from += 1;
> > +		} else {
> > +			last_spec += 1;
> > +		}
> > +	}
> > +
> > +	/* !!! Home run !!! */
> > +	if (likely(last_spec == nb_objs)) {
> > +		rte_node_next_stream_move(graph, node, next_index);
> > +		return nb_objs;
> > +	}
> > +	held += last_spec;
> > +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > +	rte_node_next_stream_put(graph, node, next_index, held);
> 
> OK.  Forget my comments in different mail about difference between
> encode/put - I got it now.
> 
> > +
> > +	return nb_objs;
> > +}
> > +
> [...]
> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-04-10  5:09           ` Nithin Dabilpuram
  2020-04-10  8:22             ` Nithin Dabilpuram
@ 2020-04-10 12:52             ` Andrzej Ostruszka
  2020-04-10 14:54               ` [dpdk-dev] [EXT] " Nithin Dabilpuram
  1 sibling, 1 reply; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-10 12:52 UTC (permalink / raw)
  To: Nithin Dabilpuram; +Cc: dev

On 4/10/20 7:09 AM, Nithin Dabilpuram wrote:
> On Fri, Apr 10, 2020 at 01:07:17AM +0200, Andrzej Ostruszka wrote:
[...]
>>> +struct rte_node_mbuf_priv2 {
>>> +	union {
>>> +		/* Sym crypto */
>>> +		struct {
>>> +			struct rte_crypto_op op;
>>> +		};
>>> +	};
>>> +} __rte_cache_aligned;
>>
>> Why such definition?

The question was more on "technicalities" - you have struct with anon
union with anon struct with a struct.  Why such deep nesting - I guess
the union is there for the possible future extensions but the next anon
struct - what is it for?

> For communication b/w nodes, we need some per mbuf private space.
> We defined it into two halfs for performance reasons as
> #1 rte_node_mbuf_priv1(8 bytes) mapped to mbuf->udata64
> #2 rte_node_mbuf_priv2(RTE_CACHE_LINE_SIZE bytes) mapped to mbuf private area.
> 
> #1 is smaller area and will not have a cache miss when accessed as mbuf
> is already in cache.
> #2 is larger area and probably good enough for many use cases like ipsec, crypto 
> etc, and there will be an extra cost of cache miss to access it.
> 
> Atleast in OCTEONTX2, we are able to see 27% performance drop, if use single
> private area #2 for everything instead.
> 
> Since pkt_mbuf pool are created by application, we these structures are defined
> here have a check in ctrl api if the pkt_mbuf pool meets the mbuf private area
> size requirement.

Thank you for explanations.

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node
  2020-04-10 10:20           ` Nithin Dabilpuram
@ 2020-04-10 14:41             ` Nithin Dabilpuram
  2020-04-10 15:17               ` Andrzej Ostruszka
  0 siblings, 1 reply; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10 14:41 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 03:50:06PM +0530, Nithin Dabilpuram wrote:
> On Fri, Apr 10, 2020 at 01:07:34AM +0200, Andrzej Ostruszka wrote:
> > On 4/5/20 10:56 AM, jerinj@marvell.com wrote:
> > > From: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > > 
> > > Add IPv4 lookup process function for ip4_lookup node.
> > > This node performs LPM lookup using simple RTE_LPM API on every packet
> > > received and forwards it to a next node that is identified by lookup
> > > result.
> > > 
> > > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > [...]
> > > +static uint16_t
> > > +ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
> > > +			void **objs, uint16_t nb_objs)
> > > +{
> > > +	struct rte_ipv4_hdr *ipv4_hdr;
> > > +	void **to_next, **from;
> > > +	uint16_t last_spec = 0;
> > > +	struct rte_mbuf *mbuf;
> > > +	rte_edge_t next_index;
> > > +	struct rte_lpm *lpm;
> > > +	uint16_t held = 0;
> > > +	uint32_t drop_nh;
> > > +	int i, rc;
> > > +
> > > +	/* Speculative next */
> > > +	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
> > > +	/* Drop node */
> > > +	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
> > > +
> > > +	/* Get socket specific LPM from ctx */
> > > +	lpm = *((struct rte_lpm **)node->ctx);
> > > +	from = objs;
> > > +
> > > +	/* Get stream for the speculated next node */
> > > +	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
> > > +	for (i = 0; i < nb_objs; i++) {
> > > +		uint32_t next_hop;
> > > +		uint16_t next;
> > > +
> > > +		mbuf = (struct rte_mbuf *)objs[i];
> > > +
> > > +		/* Extract DIP of mbuf0 */
> > > +		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
> > > +				sizeof(struct rte_ether_hdr));
> > > +		/* Extract cksum, ttl as ipv4 hdr is in cache */
> > > +		rte_node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
> > > +		rte_node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
> > > +
> > > +		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
> > > +				    &next_hop);
> > > +		next_hop = (rc == 0) ? next_hop : drop_nh;
> > 
> > Maybe simple if here?  I see the same in other patches.
> 
> Will fix it in V5.

I now see it is better to leave this statement 
instead of putting an "if(unlikely(rc))" that might force an
explicit branch instruction. On other hand, conditional might
be making use of an "csel" instruction in arm64 or "cmov" in x86.
Are you ok with leaving this line as it is ? or you have a strong opinion
to change it to if ?


> > 
> > > +
> > > +		rte_node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
> > > +		next_hop = next_hop >> 16;
> > > +		next = (uint16_t)next_hop;
> > > +
> > > +		if (unlikely(next_index != next)) {
> > > +			/* Copy things successfully speculated till now */
> > > +			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > > +			from += last_spec;
> > > +			to_next += last_spec;
> > > +			held += last_spec;
> > > +			last_spec = 0;
> > > +
> > > +			rte_node_enqueue_x1(graph, node, next, from[0]);
> > > +			from += 1;
> > > +		} else {
> > > +			last_spec += 1;
> > > +		}
> > > +	}
> > > +
> > > +	/* !!! Home run !!! */
> > > +	if (likely(last_spec == nb_objs)) {
> > > +		rte_node_next_stream_move(graph, node, next_index);
> > > +		return nb_objs;
> > > +	}
> > > +	held += last_spec;
> > > +	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
> > > +	rte_node_next_stream_put(graph, node, next_index, held);
> > 
> > OK.  Forget my comments in different mail about difference between
> > encode/put - I got it now.
> > 
> > > +
> > > +	return nb_objs;
> > > +}
> > > +
> > [...]
> > 
> > With regards
> > Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-04-10 12:52             ` Andrzej Ostruszka
@ 2020-04-10 14:54               ` Nithin Dabilpuram
  0 siblings, 0 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-04-10 14:54 UTC (permalink / raw)
  To: Andrzej Ostruszka; +Cc: dev

On Fri, Apr 10, 2020 at 02:52:33PM +0200, Andrzej Ostruszka wrote:
> External Email
> 
> ----------------------------------------------------------------------
> On 4/10/20 7:09 AM, Nithin Dabilpuram wrote:
> > On Fri, Apr 10, 2020 at 01:07:17AM +0200, Andrzej Ostruszka wrote:
> [...]
> >>> +struct rte_node_mbuf_priv2 {
> >>> +	union {
> >>> +		/* Sym crypto */
> >>> +		struct {
> >>> +			struct rte_crypto_op op;
> >>> +		};
> >>> +	};
> >>> +} __rte_cache_aligned;
> >>
> >> Why such definition?
> 
> The question was more on "technicalities" - you have struct with anon
> union with anon struct with a struct.  Why such deep nesting - I guess
> the union is there for the possible future extensions but the next anon
> struct - what is it for?

I think inner struct helps in collecting together a specific node's data like
priv1. For example

struct node_mbuf_priv2 {
	union {
		/* Sym crypto */
		struct {
			struct rte_crypto_op op;
			uint64_t extra_session_info;
		};

		/* Reassembly info */
		struct {
			uint64_t reassembly_info;
			uint64_t pad;
		};
	};

	uint8_t data[64];
} __rte_cache_aligned;


Another thing is given that currently there is no crypto support, I'm removing 
the current content of struct node_mbuf_priv2 and just leaving a pad field
for future expansion purpose.

> 
> > For communication b/w nodes, we need some per mbuf private space.
> > We defined it into two halfs for performance reasons as
> > #1 rte_node_mbuf_priv1(8 bytes) mapped to mbuf->udata64
> > #2 rte_node_mbuf_priv2(RTE_CACHE_LINE_SIZE bytes) mapped to mbuf private area.
> > 
> > #1 is smaller area and will not have a cache miss when accessed as mbuf
> > is already in cache.
> > #2 is larger area and probably good enough for many use cases like ipsec, crypto 
> > etc, and there will be an extra cost of cache miss to access it.
> > 
> > Atleast in OCTEONTX2, we are able to see 27% performance drop, if use single
> > private area #2 for everything instead.
> > 
> > Since pkt_mbuf pool are created by application, we these structures are defined
> > here have a check in ctrl api if the pkt_mbuf pool meets the mbuf private area
> > size requirement.
> 
> Thank you for explanations.
> 
> With regards
> Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node
  2020-04-10 14:41             ` Nithin Dabilpuram
@ 2020-04-10 15:17               ` Andrzej Ostruszka
  0 siblings, 0 replies; 219+ messages in thread
From: Andrzej Ostruszka @ 2020-04-10 15:17 UTC (permalink / raw)
  To: Nithin Dabilpuram; +Cc: dev

On 4/10/20 4:41 PM, Nithin Dabilpuram wrote:
> On Fri, Apr 10, 2020 at 03:50:06PM +0530, Nithin Dabilpuram wrote:
>> On Fri, Apr 10, 2020 at 01:07:34AM +0200, Andrzej Ostruszka wrote:
[...]
>>>> +		next_hop = (rc == 0) ? next_hop : drop_nh;
>>>
>>> Maybe simple if here?  I see the same in other patches.
>>
>> Will fix it in V5.
> 
> I now see it is better to leave this statement 
> instead of putting an "if(unlikely(rc))" that might force an
> explicit branch instruction. On other hand, conditional might
> be making use of an "csel" instruction in arm64 or "cmov" in x86.
> Are you ok with leaving this line as it is ? or you have a strong opinion
> to change it to if ?

It is fine - if you think the generated code will be better then leave it.

With regards
Andrzej Ostruszka

^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 00/29] graph: introduce graph subsystem
  2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
                         ` (29 preceding siblings ...)
  2020-04-09 23:13       ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem Andrzej Ostruszka
@ 2020-04-11 14:13       ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 01/29] graph: define the public API for graph support jerinj
                           ` (30 more replies)
  30 siblings, 31 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:13 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang, amo, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Using graph traversal for packet processing is a proven architecture
that has been implemented in various open source libraries.

Graph architecture for packet processing enables abstracting the data
processing functions as “nodes” and “links” them together to create a
complex “graph” to create reusable/modular data processing functions. 

The patchset further includes performance enhancements and modularity
to the DPDK as discussed in more detail below.

v5..v4:
------
Addressed the following review comments from Andrzej Ostruszka.

1) Addressed and comment in (http://mails.dpdk.org/archives/dev/2020-April/162184.html)
and improved following function prototypes/return types and adjusted the
implementation
a) rte_graph_node_get
b) rte_graph_max_count
c) rte_graph_export
d) rte_graph_destroy
2) Updated UT and l3fwd-graph for updated function prototype
3) bug fix in edge_update
4) avoid reading graph_src_nodes_count() twice in rte_graph_create()
5) Fix graph_mem_fixup_secondray typo
6) Fixed Doxygen comments for rte_node_next_stream_put
7) Updated the documentation to reflect the same.
8) Removed RTE prefix from rte_node_mbuf_priv[1|2] * as they are
internal defines
9) Limited next_hop id provided to LPM route add in
librte_node/ip4_lookup.c to 24 bits ()
10) Fixed pattern array overflow issue with l3fwd-graph/main.c by
splitting pattern
array to default + non-default array. Updated doc with the same info.
11) Fixed parsing issues in parse_config() in l3fwd-graph/main.c inline
with issues reported
in l2fwd-event
12)Removed next_hop field in l3fwd-graph/main.c main()
13) Fixed graph create error check in l3fwd-graph/main.c main()

v4..v3:
-------
Addressed the following review comments from Wang, Xiao W

1) Remove unnecessary line from rte_graph.h
2) Fix a typo from rte_graph.h
3) Move NODE_ID_CHECK to 3rd patch where it is first used.
4) Fixed bug in edge_update()

v3..v2:
-------
1) refactor ipv4 node lookup by moving SSE and NEON specific code to
lib/librte_node/ip4_lookup_sse.h and lib/librte_node/ip4_lookup_neon.h
2) Add scalar version of process() function for ipv4 lookup to make
the node work on NON x86 and arm64 machines.

v2..v1:
------
1) Added programmer guide/implementation documentation and l3fwd-graph doc

RFC..v1:
--------

1) Split the patch to more logical ones for review.
2) Added doxygen comments for the API
3) Code cleanup
4) Additional performance improvements.
Delta between l3fwd and l3fwd-graph is negligible now.
(~1%) on octeontx2.
5) Added SIMD routines for x86 in additional to arm64.

Hosted in netlify for easy reference:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Programmer’s Guide:
https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html

l3fwd-graph doc:
https://dpdk-graph.netlify.com/doc/html/guides/sample_app_ug/l3_forward_graph.html

API doc:
https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__graph__worker_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__eth__api_8h.html
https://dpdk-graph.netlify.com/doc/html/api/rte__node__ip4__api_8h.html

2) Added the release notes for the this feature

3) Fix build issues reported by CI for v1:
http://mails.dpdk.org/archives/test-report/2020-March/121326.html


Addional nodes planned for v20.08
----------------------------------
1) Packet classification node
2) Support for IPV6 LPM node


This patchset contains
-----------------------------
1) The API definition to "create" nodes and "link" together to create a
"graph" for packet processing. See, lib/librte_graph/rte_graph.h  

2) The Fast path API definition for the graph walker and enqueue
function used by the workers. See, lib/librte_graph/rte_graph_worker.h

3) Optimized SW implementation for (1) and (2). See, lib/librte_graph/

4) Test case to verify the graph infrastructure functionality
See, app/test/test_graph.c
 
5) Performance test cases to evaluate the cost of graph walker and nodes
enqueue fast-path function for various combinations.

See app/test/test_graph_perf.c

6) Packet processing nodes(Null, Rx, Tx, Pkt drop, IPV4 rewrite, IPv4
lookup)
using graph infrastructure. See lib/librte_node/*

7) An example application to showcase l3fwd
(functionality same as existing examples/l3fwd) using graph
infrastructure and use packets processing nodes (item (6)). See examples/l3fwd-graph/.

Performance
-----------
1) Graph walk and node enqueue overhead can be tested with performance
test case application [1]
# If all packets go from a node to another node (we call it as
# "homerun") then it will be just a pointer swap for a burst of packets.
# In the worst case, a couple of handful cycles to move an object from a
node to another node.

2) Performance comparison with existing l3fwd (The complete static code
with out any nodes) vs modular l3fwd-graph with 5 nodes
(ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
Here is graphical representation of the l3fwd-graph as Graphviz dot
file: 
http://bit.ly/39UPPGm

# l3fwd-graph performance is -1.2% wrt static l3fwd.

# We have simulated the similar test with existing librte_pipeline
# application [4].
ip_pipline application is -48.62% wrt static l3fwd.

The above results are on octeontx2. It may vary on other platforms.
The platforms with higher L1 and L2 caches will have further better
performance.


Tested architectures:
--------------------
1) AArch64
2) X86


Identified tweaking for better performance on different targets
---------------------------------------------------------------
1) Test with various burst size values (256, 128, 64, 32) using
CONFIG_RTE_GRAPH_BURST_SIZE config option.
Based on our testing, on x86 and arm64 servers, The sweet spot is 256
burst size.
While on arm64 embedded SoCs, it is either 64 or 128.

2) Disable node statistics (use CONFIG_RTE_LIBRTE_GRAPH_STATS config
option)
if not needed.

3) Use arm64 optimized memory copy for arm64 architecture by
selecting CONFIG_RTE_ARCH_ARM64_MEMCPY. 

Commands to run tests
---------------------

[1] 
perf test:
echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[2]
functionality test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

[3]
l3fwd-graph:
./l3fwd-graph -c 0x100  -- -p 0x3 --config="(0, 0, 8)" -P

[4]
# ./ip_pipeline --c 0xff0000 -- -s route.cli

Route.cli: (Copy paste to the shell to avoid dos format issues)

https://pastebin.com/raw/B4Ktx7TT

Jerin Jacob (13):
  graph: define the public API for graph support
  graph: implement node registration
  graph: implement node operations
  graph: implement node debug routines
  graph: implement internal graph operation helpers
  graph: populate fastpath memory for graph reel
  graph: implement create and destroy APIs
  graph: implement graph operation APIs
  graph: implement Graphviz export
  graph: implement debug routines
  graph: implement stats support
  graph: implement fastpath API routines
  doc: add graph library programmer's guide guide

Kiran Kumar K (2):
  graph: add unit test case
  node: add ipv4 rewrite node

Nithin Dabilpuram (11):
  node: add log infra and null node
  node: add ethdev Rx node
  node: add ethdev Tx node
  node: add ethdev Rx and Tx node ctrl API
  node: ipv4 lookup for arm64
  node: add ipv4 rewrite and lookup ctrl API
  node: add packet drop node
  l3fwd-graph: add graph based l3fwd skeleton
  l3fwd-graph: add ethdev configuration changes
  l3fwd-graph: add graph config and main loop
  doc: add l3fwd graph application user guide

Pavan Nikhilesh (3):
  graph: add performance testcase
  node: add generic ipv4 lookup node
  node: ipv4 lookup for x86

 MAINTAINERS                                   |   14 +
 app/test/Makefile                             |    7 +
 app/test/meson.build                          |   12 +-
 app/test/test_graph.c                         |  819 ++++
 app/test/test_graph_perf.c                    | 1057 ++++++
 config/common_base                            |   12 +
 config/rte_config.h                           |    4 +
 doc/api/doxy-api-index.md                     |    5 +
 doc/api/doxy-api.conf.in                      |    2 +
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   32 +
 doc/guides/sample_app_ug/index.rst            |    1 +
 doc/guides/sample_app_ug/intro.rst            |    4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst |  334 ++
 examples/Makefile                             |    3 +
 examples/l3fwd-graph/Makefile                 |   58 +
 examples/l3fwd-graph/main.c                   | 1126 ++++++
 examples/l3fwd-graph/meson.build              |   13 +
 examples/meson.build                          |    6 +-
 lib/Makefile                                  |    6 +
 lib/librte_graph/Makefile                     |   28 +
 lib/librte_graph/graph.c                      |  587 +++
 lib/librte_graph/graph_debug.c                |   84 +
 lib/librte_graph/graph_ops.c                  |  169 +
 lib/librte_graph/graph_populate.c             |  234 ++
 lib/librte_graph/graph_private.h              |  347 ++
 lib/librte_graph/graph_stats.c                |  406 ++
 lib/librte_graph/meson.build                  |   11 +
 lib/librte_graph/node.c                       |  421 +++
 lib/librte_graph/rte_graph.h                  |  668 ++++
 lib/librte_graph/rte_graph_version.map        |   47 +
 lib/librte_graph/rte_graph_worker.h           |  510 +++
 lib/librte_node/Makefile                      |   32 +
 lib/librte_node/ethdev_ctrl.c                 |  115 +
 lib/librte_node/ethdev_rx.c                   |  221 ++
 lib/librte_node/ethdev_rx_priv.h              |   81 +
 lib/librte_node/ethdev_tx.c                   |   86 +
 lib/librte_node/ethdev_tx_priv.h              |   62 +
 lib/librte_node/ip4_lookup.c                  |  215 ++
 lib/librte_node/ip4_lookup_neon.h             |  238 ++
 lib/librte_node/ip4_lookup_sse.h              |  244 ++
 lib/librte_node/ip4_rewrite.c                 |  326 ++
 lib/librte_node/ip4_rewrite_priv.h            |   77 +
 lib/librte_node/log.c                         |   14 +
 lib/librte_node/meson.build                   |   10 +
 lib/librte_node/node_private.h                |   79 +
 lib/librte_node/null.c                        |   23 +
 lib/librte_node/pkt_drop.c                    |   26 +
 lib/librte_node/rte_node_eth_api.h            |   64 +
 lib/librte_node/rte_node_ip4_api.h            |   78 +
 lib/librte_node/rte_node_version.map          |    9 +
 lib/meson.build                               |    5 +-
 meson.build                                   |    1 +
 mk/rte.app.mk                                 |    2 +
 58 files changed, 14538 insertions(+), 5 deletions(-)
 create mode 100644 app/test/test_graph.c
 create mode 100644 app/test/test_graph_perf.c
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/graph_debug.c
 create mode 100644 lib/librte_graph/graph_ops.c
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/graph_stats.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/node.c
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map
 create mode 100644 lib/librte_graph/rte_graph_worker.h
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/ip4_lookup_neon.h
 create mode 100644 lib/librte_node/ip4_lookup_sse.h
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/pkt_drop.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h
 create mode 100644 lib/librte_node/rte_node_ip4_api.h
 create mode 100644 lib/librte_node/rte_node_version.map

-- 
2.25.1


^ permalink raw reply	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 01/29] graph: define the public API for graph support
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 02/29] graph: implement node registration jerinj
                           ` (29 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Thomas Monjalon, Bruce Richardson, John McNamara,
	Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Graph architecture abstracts the data processing functions as
"node" and "link" them together to create a complex "graph" to enable
reusable/modular data processing functions.

These APIs enables graph framework operations such as create, lookup,
dump and destroy on graph and node operations such as clone,
edge update, and edge shrink, etc. The API also allows creating the
stats cluster to monitor per graph and per node stats.

This patch defines the public API for graph support.
This patch also adds support for the build infrastructure and
update the MAINTAINERS file for the graph subsystem.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                            |   5 +
 config/common_base                     |   7 +
 config/rte_config.h                    |   4 +
 doc/api/doxy-api-index.md              |   1 +
 doc/api/doxy-api.conf.in               |   1 +
 lib/Makefile                           |   3 +
 lib/librte_graph/Makefile              |  22 +
 lib/librte_graph/graph.c               |   5 +
 lib/librte_graph/meson.build           |  11 +
 lib/librte_graph/rte_graph.h           | 668 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   3 +
 lib/meson.build                        |   2 +-
 mk/rte.app.mk                          |   1 +
 13 files changed, 732 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/Makefile
 create mode 100644 lib/librte_graph/graph.c
 create mode 100644 lib/librte_graph/meson.build
 create mode 100644 lib/librte_graph/rte_graph.h
 create mode 100644 lib/librte_graph/rte_graph_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 4800f6884..aa40cc92b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1462,6 +1462,11 @@ F: examples/bpf/
 F: app/test/test_bpf.c
 F: doc/guides/prog_guide/bpf_lib.rst
 
+Graph - EXPERIMENTAL
+M: Jerin Jacob <jerinj@marvell.com>
+M: Kiran Kumar K <kirankumark@marvell.com>
+F: lib/librte_graph/
+
 
 Test Applications
 -----------------
diff --git a/config/common_base b/config/common_base
index c31175f9d..32f982136 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1074,6 +1074,13 @@ CONFIG_RTE_LIBRTE_BPF_ELF=n
 #
 CONFIG_RTE_LIBRTE_IPSEC=y
 
+#
+# Compile librte_graph
+#
+CONFIG_RTE_LIBRTE_GRAPH=y
+CONFIG_RTE_GRAPH_BURST_SIZE=256
+CONFIG_RTE_LIBRTE_GRAPH_STATS=y
+
 #
 # Compile the test application
 #
diff --git a/config/rte_config.h b/config/rte_config.h
index d30786bc0..e9201fd46 100644
--- a/config/rte_config.h
+++ b/config/rte_config.h
@@ -98,6 +98,10 @@
 /* KNI defines */
 #define RTE_KNI_PREEMPT_DEFAULT 1
 
+/* rte_graph defines */
+#define RTE_GRAPH_BURST_SIZE 256
+#define RTE_LIBRTE_GRAPH_STATS 1
+
 /****** driver defines ********/
 
 /* QuickAssist device */
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index dff496be0..5cc50f750 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -159,6 +159,7 @@ The public API headers are grouped by topics:
   * [pipeline]         (@ref rte_pipeline.h)
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
+  * [graph]            (@ref rte_graph.h):
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index 65e8146be..e3b7f54f8 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -33,6 +33,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_eventdev \
                           @TOPDIR@/lib/librte_fib \
                           @TOPDIR@/lib/librte_flow_classify \
+                          @TOPDIR@/lib/librte_graph \
                           @TOPDIR@/lib/librte_gro \
                           @TOPDIR@/lib/librte_gso \
                           @TOPDIR@/lib/librte_hash \
diff --git a/lib/Makefile b/lib/Makefile
index 46b91ae1a..1f572b659 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -119,6 +119,9 @@ DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
 DIRS-$(CONFIG_RTE_LIBRTE_RCU) += librte_rcu
 DEPDIRS-librte_rcu := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
+DEPDIRS-librte_graph := librte_eal
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
new file mode 100644
index 000000000..26fe514f3
--- /dev/null
+++ b/lib/librte_graph/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_graph.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+LDLIBS += -lrte_eal
+
+EXPORT_MAP := rte_graph_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
new file mode 100644
index 000000000..a55bf443a
--- /dev/null
+++ b/lib/librte_graph/graph.c
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "rte_graph.h"
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
new file mode 100644
index 000000000..455cf2ba5
--- /dev/null
+++ b/lib/librte_graph/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+name = 'graph'
+
+sources = files('graph.c')
+headers = files('rte_graph.h')
+allow_experimental_apis = true
+
+deps += ['eal']
diff --git a/lib/librte_graph/rte_graph.h b/lib/librte_graph/rte_graph.h
new file mode 100644
index 000000000..9a26ffc18
--- /dev/null
+++ b/lib/librte_graph/rte_graph.h
@@ -0,0 +1,668 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_H_
+#define _RTE_GRAPH_H_
+
+/**
+ * @file rte_graph.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Graph architecture abstracts the data processing functions as
+ * "node" and "link" them together to create a complex "graph" to enable
+ * reusable/modular data processing functions.
+ *
+ * This API enables graph framework operations such as create, lookup,
+ * dump and destroy on graph and node operations such as clone,
+ * edge update, and edge shrink, etc. The API also allows to create the stats
+ * cluster to monitor per graph and per node stats.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <rte_common.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_GRAPH_NAMESIZE 64 /**< Max length of graph name. */
+#define RTE_NODE_NAMESIZE 64  /**< Max length of node name. */
+#define RTE_GRAPH_OFF_INVALID UINT32_MAX /**< Invalid graph offset. */
+#define RTE_NODE_ID_INVALID UINT32_MAX   /**< Invalid node id. */
+#define RTE_EDGE_ID_INVALID UINT16_MAX   /**< Invalid edge id. */
+#define RTE_GRAPH_ID_INVALID UINT16_MAX  /**< Invalid graph id. */
+#define RTE_GRAPH_FENCE 0xdeadbeef12345678ULL /**< Graph fence data. */
+
+typedef uint32_t rte_graph_off_t;  /**< Graph offset type. */
+typedef uint32_t rte_node_t;       /**< Node id type. */
+typedef uint16_t rte_edge_t;       /**< Edge id type. */
+typedef uint16_t rte_graph_t;      /**< Graph id type. */
+
+/** Burst size in terms of log2 */
+#if RTE_GRAPH_BURST_SIZE == 1
+#define RTE_GRAPH_BURST_SIZE_LOG2 0  /**< Object burst size of 1. */
+#elif RTE_GRAPH_BURST_SIZE == 2
+#define RTE_GRAPH_BURST_SIZE_LOG2 1  /**< Object burst size of 2. */
+#elif RTE_GRAPH_BURST_SIZE == 4
+#define RTE_GRAPH_BURST_SIZE_LOG2 2  /**< Object burst size of 4. */
+#elif RTE_GRAPH_BURST_SIZE == 8
+#define RTE_GRAPH_BURST_SIZE_LOG2 3  /**< Object burst size of 8. */
+#elif RTE_GRAPH_BURST_SIZE == 16
+#define RTE_GRAPH_BURST_SIZE_LOG2 4  /**< Object burst size of 16. */
+#elif RTE_GRAPH_BURST_SIZE == 32
+#define RTE_GRAPH_BURST_SIZE_LOG2 5  /**< Object burst size of 32. */
+#elif RTE_GRAPH_BURST_SIZE == 64
+#define RTE_GRAPH_BURST_SIZE_LOG2 6  /**< Object burst size of 64. */
+#elif RTE_GRAPH_BURST_SIZE == 128
+#define RTE_GRAPH_BURST_SIZE_LOG2 7  /**< Object burst size of 128. */
+#elif RTE_GRAPH_BURST_SIZE == 256
+#define RTE_GRAPH_BURST_SIZE_LOG2 8  /**< Object burst size of 256. */
+#else
+#error "Unsupported burst size"
+#endif
+
+/* Forward declaration */
+struct rte_node;  /**< Node object */
+struct rte_graph; /**< Graph object */
+struct rte_graph_cluster_stats;      /**< Stats for Cluster of graphs */
+struct rte_graph_cluster_node_stats; /**< Node stats within cluster of graphs */
+
+/**
+ * Node process function.
+ *
+ * The function invoked when the worker thread walks on nodes using
+ * rte_graph_walk().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param objs
+ *   Pointer to an array of objects to be processed.
+ * @param nb_objs
+ *   Number of objects in the array.
+ *
+ * @return
+ *   Number of objects processed.
+ *
+ * @see rte_graph_walk()
+ *
+ */
+typedef uint16_t (*rte_node_process_t)(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs);
+
+/**
+ * Node initialization function.
+ *
+ * The function invoked when the user creates the graph using rte_graph_create()
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ *
+ * @see rte_graph_create()
+ */
+typedef int (*rte_node_init_t)(const struct rte_graph *graph,
+			       struct rte_node *node);
+
+/**
+ * Node finalization function.
+ *
+ * The function invoked when the user destroys the graph using
+ * rte_graph_destroy().
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ *
+ * @see rte_graph_destroy()
+ */
+typedef void (*rte_node_fini_t)(const struct rte_graph *graph,
+				struct rte_node *node);
+
+/**
+ * Graph cluster stats callback.
+ *
+ * @param is_first
+ *   Flag to denote that stats are of the first node.
+ * @param is_last
+ *   Flag to denote that stats are of the last node.
+ * @param cookie
+ *   Cookie supplied during stats creation.
+ * @param stats
+ *   Node cluster stats data.
+ *
+ * @return
+ *   - 0: Success.
+ *   -<0: Failure.
+ */
+typedef int (*rte_graph_cluster_stats_cb_t)(bool is_first, bool is_last,
+	     void *cookie, const struct rte_graph_cluster_node_stats *stats);
+
+/**
+ * Structure to hold configuration parameters for creating the graph.
+ *
+ * @see rte_graph_create()
+ */
+struct rte_graph_param {
+	int socket_id; /**< Socket id where memory is allocated. */
+	uint16_t nb_node_patterns;  /**< Number of node patterns. */
+	const char **node_patterns;
+	/**< Array of node patterns based on shell pattern. */
+};
+
+/**
+ * Structure to hold configuration parameters for graph cluster stats create.
+ *
+ * @see rte_graph_cluster_stats_create()
+ */
+struct rte_graph_cluster_stats_param {
+	int socket_id;
+	/**< Socket id where memory is allocated */
+	rte_graph_cluster_stats_cb_t fn;
+	/**< Stats print callback function. NULL value allowed, in that case,
+	 *   default print stat function used.
+	 */
+	RTE_STD_C11
+	union {
+		void *cookie;
+		FILE *f; /**< File pointer to dump the stats when fn == NULL. */
+	};
+	uint16_t nb_graph_patterns;  /**< Number of graph patterns. */
+	const char **graph_patterns;
+	/**< Array of graph patterns based on shell pattern. */
+};
+
+/**
+ * Node cluster stats data structure.
+ *
+ * @see struct rte_graph_cluster_stats_param::fn
+ */
+struct rte_graph_cluster_node_stats {
+	uint64_t ts;	    /**< Current timestamp. */
+	uint64_t calls;	    /**< Current number of calls made. */
+	uint64_t objs;      /**< Current number of objs processed. */
+	uint64_t cycles;    /**< Current number of cycles. */
+
+	uint64_t prev_ts;	/**< Previous call timestamp. */
+	uint64_t prev_calls;	/**< Previous number of calls. */
+	uint64_t prev_objs;	/**< Previous number of processed objs. */
+	uint64_t prev_cycles;	/**< Previous number of cycles. */
+
+	uint64_t realloc_count; /**< Realloc count. */
+
+	rte_node_t id;	/**< Node identifier of stats. */
+	uint64_t hz;	/**< Cycles per seconds. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+} __rte_cache_aligned;
+
+/**
+ * Create Graph.
+ *
+ * Create memory reel, detect loops and find isolated nodes.
+ *
+ * @param name
+ *   Unique name for this graph.
+ * @param prm
+ *   Graph parameter, includes node names and count to be included
+ *   in this graph.
+ *
+ * @return
+ *   Unique graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_create(const char *name, struct rte_graph_param *prm);
+
+/**
+ * Destroy Graph.
+ *
+ * Free Graph memory reel.
+ *
+ * @param id
+ *   id of the graph to destroy.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+int rte_graph_destroy(rte_graph_t id);
+
+/**
+ * Get graph id from graph name.
+ *
+ * @param name
+ *   Name of the graph to get id.
+ *
+ * @return
+ *   Graph id on success, RTE_GRAPH_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_graph_t rte_graph_from_name(const char *name);
+
+/**
+ * Get graph name from graph id.
+ *
+ * @param id
+ *   id of the graph to get name.
+ *
+ * @return
+ *   Graph name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_graph_id_to_name(rte_graph_t id);
+
+/**
+ * Export the graph as graph viz dot file
+ *
+ * @param name
+ *   Name of the graph to export.
+ * @param f
+ *   File pointer to export the graph.
+ *
+ * @return
+ *   0 on success, error otherwise.
+ */
+__rte_experimental
+int rte_graph_export(const char *name, FILE *f);
+
+/**
+ * Get graph object from its name.
+ *
+ * Typical usage of this API to get graph objects in the worker thread and
+ * followed calling rte_graph_walk() in a loop.
+ *
+ * @param name
+ *   Name of the graph.
+ *
+ * @return
+ *   Graph pointer on success, NULL otherwise.
+ *
+ * @see rte_graph_walk()
+ */
+__rte_experimental
+struct rte_graph *rte_graph_lookup(const char *name);
+
+/**
+ * Get maximum number of graph available.
+ *
+ * @return
+ *   Maximum graph count.
+ */
+__rte_experimental
+rte_graph_t rte_graph_max_count(void);
+
+/**
+ * Dump the graph information to file.
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param id
+ *   Graph id to get graph info.
+ */
+__rte_experimental
+void rte_graph_dump(FILE *f, rte_graph_t id);
+
+/**
+ * Dump all graphs information to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ */
+__rte_experimental
+void rte_graph_list_dump(FILE *f);
+
+/**
+ * Dump graph information along with node info to file
+ *
+ * @param f
+ *   File pointer to dump graph info.
+ * @param graph
+ *   Graph pointer to get graph info.
+ * @param all
+ *   true to dump nodes in the graph.
+ */
+__rte_experimental
+void rte_graph_obj_dump(FILE *f, struct rte_graph *graph, bool all);
+
+/** Macro to browse rte_node object after the graph creation */
+#define rte_graph_foreach_node(count, off, graph, node)                        \
+	for (count = 0, off = graph->nodes_start,                              \
+	     node = RTE_PTR_ADD(graph, off);                                   \
+	     count < graph->nb_nodes;                                          \
+	     off = node->next, node = RTE_PTR_ADD(graph, off), count++)
+
+/**
+ * Get node object with in graph from id.
+ *
+ * @param graph_id
+ *   Graph id to get node pointer from.
+ * @param node_id
+ *   Node id to get node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get(rte_graph_t graph_id, rte_node_t node_id);
+
+/**
+ * Get node pointer with in graph from name.
+ *
+ * @param graph
+ *   Graph name to get node pointer from.
+ * @param name
+ *   Node name to get the node pointer.
+ *
+ * @return
+ *   Node pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_node *rte_graph_node_get_by_name(const char *graph,
+					    const char *name);
+
+/**
+ * Create graph stats cluster to aggregate runtime node stats.
+ *
+ * @param prm
+ *   Parameters including file pointer to dump stats,
+ *   Graph pattern to create cluster and callback function.
+ *
+ * @return
+ *   Valid pointer on success, NULL otherwise.
+ */
+__rte_experimental
+struct rte_graph_cluster_stats *rte_graph_cluster_stats_create(
+			const struct rte_graph_cluster_stats_param *prm);
+
+/**
+ * Destroy cluster stats.
+ *
+ * @param stat
+ *    Valid cluster pointer to destroy.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat);
+
+/**
+ * Get stats to application.
+ *
+ * @param[out] stat
+ *   Cluster status.
+ * @param skip_cb
+ *   true to skip callback function invocation.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat,
+				 bool skip_cb);
+
+/**
+ * Reset cluster stats to zero.
+ *
+ * @param stat
+ *   Valid cluster stats pointer.
+ */
+__rte_experimental
+void rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat);
+
+/**
+ * Structure defines the node registration parameters.
+ *
+ * @see __rte_node_register(), RTE_NODE_REGISTER()
+ */
+struct rte_node_register {
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+#define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
+	rte_node_process_t process; /**< Node process function. */
+	rte_node_init_t init;       /**< Node init function. */
+	rte_node_fini_t fini;       /**< Node fini function. */
+	rte_node_t id;		    /**< Node Identifier. */
+	rte_node_t parent_id;       /**< Identifier of parent node. */
+	rte_edge_t nb_edges;        /**< Number of edges from this node. */
+	const char *next_nodes[];   /**< Names of next nodes. */
+};
+
+/**
+ * Register new packet processing node. Nodes can be registered
+ * dynamically via this call or statically via the RTE_NODE_REGISTER
+ * macro.
+ *
+ * @param node
+ *   Valid node pointer with name, process function and next_nodes.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ *
+ * @see RTE_NODE_REGISTER()
+ */
+__rte_experimental
+rte_node_t __rte_node_register(const struct rte_node_register *node);
+
+/**
+ * Register a static node.
+ *
+ * The static node is registered through the constructor scheme, thereby, it can
+ * be used in a multi-process scenario.
+ *
+ * @param node
+ *   Valid node pointer with name, process function, and next_nodes.
+ */
+#define RTE_NODE_REGISTER(node)                                                \
+	RTE_INIT(rte_node_register_##node)                                     \
+	{                                                                      \
+		node.parent_id = RTE_NODE_ID_INVALID;                          \
+		node.id = __rte_node_register(&node);                          \
+	}
+
+/**
+ * Clone a node from static node(node created from RTE_NODE_REGISTER).
+ *
+ * @param id
+ *   Static node id to clone from.
+ * @param name
+ *   Name of the new node. The library prepends the parent node name to the
+ * user-specified name. The final node name will be,
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_clone(rte_node_t id, const char *name);
+
+/**
+ * Get node id from node name.
+ *
+ * @param name
+ *   Valid node name. In the case of the cloned node, the name will be
+ * "parent node name" + "-" + name.
+ *
+ * @return
+ *   Valid node id on success, RTE_NODE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_node_t rte_node_from_name(const char *name);
+
+/**
+ * Get node name from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid node name on success, NULL otherwise.
+ */
+__rte_experimental
+char *rte_node_id_to_name(rte_node_t id);
+
+/**
+ * Get the number of edges(next-nodes) for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ *
+ * @return
+ *   Valid edge count on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_count(rte_node_t id);
+
+/**
+ * Update the edges for a node from node id.
+ *
+ * @param id
+ *   Valid node id.
+ * @param from
+ *   Index to update the edges from. RTE_EDGE_ID_INVALID is valid,
+ * in that case, it will be added to the end of the list.
+ * @param next_nodes
+ *   Name of the edges to update.
+ * @param nb_edges
+ *   Number of edges to update.
+ *
+ * @return
+ *   Valid edge count on success, 0 otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_update(rte_node_t id, rte_edge_t from,
+				const char **next_nodes, uint16_t nb_edges);
+
+/**
+ * Shrink the edges to a given size.
+ *
+ * @param id
+ *   Valid node id.
+ * @param size
+ *   New size to shrink the edges.
+ *
+ * @return
+ *   New size on success, RTE_EDGE_ID_INVALID otherwise.
+ */
+__rte_experimental
+rte_edge_t rte_node_edge_shrink(rte_node_t id, rte_edge_t size);
+
+/**
+ * Get the edge names from a given node.
+ *
+ * @param id
+ *   Valid node id.
+ * @param[out] next_nodes
+ *   Buffer to copy the edge names. The NULL value is allowed in that case,
+ * the function returns the size of the array that needs to be allocated.
+ *
+ * @return
+ *   When next_nodes == NULL, it returns the size of the array else
+ *  number of item copied.
+ */
+__rte_experimental
+rte_node_t rte_node_edge_get(rte_node_t id, char *next_nodes[]);
+
+/**
+ * Get maximum nodes available.
+ *
+ * @return
+ *   Maximum nodes count.
+ */
+__rte_experimental
+rte_node_t rte_node_max_count(void);
+
+/**
+ * Dump node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ * @param id
+ *   Node id to get the info.
+ */
+__rte_experimental
+void rte_node_dump(FILE *f, rte_node_t id);
+
+/**
+ * Dump all node info to file.
+ *
+ * @param f
+ *   File pointer to dump the node info.
+ */
+__rte_experimental
+void rte_node_list_dump(FILE *f);
+
+/**
+ * Test the validity of node id.
+ *
+ * @param id
+ *   Node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_node_is_invalid(rte_node_t id)
+{
+	return (id == RTE_NODE_ID_INVALID);
+}
+
+/**
+ * Test the validity of edge id.
+ *
+ * @param id
+ *   Edge node id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_edge_is_invalid(rte_edge_t id)
+{
+	return (id == RTE_EDGE_ID_INVALID);
+}
+
+/**
+ * Test the validity of graph id.
+ *
+ * @param id
+ *   Graph id to check.
+ *
+ * @return
+ *   1 if valid id, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_is_invalid(rte_graph_t id)
+{
+	return (id == RTE_GRAPH_ID_INVALID);
+}
+
+/**
+ * Test stats feature support.
+ *
+ * @return
+ *   1 if stats enabled, 0 otherwise.
+ */
+static __rte_always_inline int
+rte_graph_has_stats_feature(void)
+{
+#ifdef RTE_LIBRTE_GRAPH_STATS
+	return RTE_LIBRTE_GRAPH_STATS;
+#else
+	return 0;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_H_ */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
new file mode 100644
index 000000000..55ef35df5
--- /dev/null
+++ b/lib/librte_graph/rte_graph_version.map
@@ -0,0 +1,3 @@
+EXPERIMENTAL {
+
+};
diff --git a/lib/meson.build b/lib/meson.build
index 9c3cc55d5..c43d86bb9 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index d295ca0a5..b1195f09a 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -98,6 +98,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
+_LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 02/29] graph: implement node registration
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 01/29] graph: define the public API for graph support jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 03/29] graph: implement node operations jerinj
                           ` (28 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding rte_node_register() API implementation includes allocating
memory for node object, check for duplicate node name and
add the allocated node to STAILQ node_list for future use.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph.c               |  18 +++-
 lib/librte_graph/graph_private.h       |  75 ++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/node.c                | 113 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   4 +
 6 files changed, 211 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_private.h
 create mode 100644 lib/librte_graph/node.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 26fe514f3..933d0ee49 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -14,6 +14,7 @@ LDLIBS += -lrte_eal
 EXPORT_MAP := rte_graph_version.map
 
 # all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a55bf443a..a9c124896 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,4 +2,20 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
-#include "rte_graph.h"
+#include <rte_spinlock.h>
+
+#include "graph_private.h"
+
+static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+
+void
+graph_spinlock_lock(void)
+{
+	rte_spinlock_lock(&graph_lock);
+}
+
+void
+graph_spinlock_unlock(void)
+{
+	rte_spinlock_unlock(&graph_lock);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
new file mode 100644
index 000000000..8b9ff5292
--- /dev/null
+++ b/lib/librte_graph/graph_private.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_PRIVATE_H_
+#define _RTE_GRAPH_PRIVATE_H_
+
+#include <inttypes.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_eal.h>
+
+#include "rte_graph.h"
+
+/**
+ * @internal
+ *
+ * Structure that holds node internal data.
+ */
+struct node {
+	STAILQ_ENTRY(node) next;      /**< Next node in the list. */
+	char name[RTE_NODE_NAMESIZE]; /**< Name of the node. */
+	uint64_t flags;		      /**< Node configuration flag. */
+	rte_node_process_t process;   /**< Node process function. */
+	rte_node_init_t init;         /**< Node init function. */
+	rte_node_fini_t fini;	      /**< Node fini function. */
+	rte_node_t id;		      /**< Allocated identifier for the node. */
+	rte_node_t parent_id;	      /**< Parent node identifier. */
+	rte_edge_t nb_edges;	      /**< Number of edges from this node. */
+	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
+};
+
+/* Node functions */
+STAILQ_HEAD(node_head, node);
+
+/**
+ * @internal
+ *
+ * Get the head of the node list.
+ *
+ * @return
+ *   Pointer to the node head.
+ */
+struct node_head *node_list_head_get(void);
+
+/**
+ * @internal
+ *
+ * Get node pointer from node name.
+ *
+ * @param name
+ *   Pointer to character string containing the node name.
+ *
+ * @return
+ *   Pointer to the node.
+ */
+struct node *node_from_name(const char *name);
+
+/* Lock functions */
+/**
+ * @internal
+ *
+ * Take a lock on the graph internal spin lock.
+ */
+void graph_spinlock_lock(void);
+
+/**
+ * @internal
+ *
+ * Release a lock on the graph internal spin lock.
+ */
+void graph_spinlock_unlock(void);
+
+#endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 455cf2ba5..5754ac23b 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('graph.c')
+sources = files('node.c', 'graph.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
new file mode 100644
index 000000000..336cd1c94
--- /dev/null
+++ b/lib/librte_graph/node.c
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_string_fns.h>
+
+#include "graph_private.h"
+
+static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
+static rte_node_t node_id;
+
+/* Private functions */
+struct node_head *
+node_list_head_get(void)
+{
+	return &node_list;
+}
+
+struct node *
+node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static bool
+node_has_duplicate_entry(const char *name)
+{
+	struct node *node;
+
+	/* Is duplicate name registered */
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0) {
+			rte_errno = EEXIST;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Public functions */
+rte_node_t
+__rte_node_register(const struct rte_node_register *reg)
+{
+	struct node *node;
+	rte_edge_t i;
+	size_t sz;
+
+	graph_spinlock_lock();
+
+	/* Check sanity */
+	if (reg == NULL || reg->process == NULL) {
+		rte_errno = EINVAL;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(reg->name))
+		goto fail;
+
+	sz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);
+	node = calloc(1, sz);
+	if (node == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Initialize the node */
+	if (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {
+		rte_errno = E2BIG;
+		goto free;
+	}
+	node->flags = reg->flags;
+	node->process = reg->process;
+	node->init = reg->init;
+	node->fini = reg->fini;
+	node->nb_edges = reg->nb_edges;
+	node->parent_id = reg->parent_id;
+	for (i = 0; i < reg->nb_edges; i++) {
+		if (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto free;
+		}
+	}
+
+	node->id = node_id++;
+
+	/* Add the node at tail */
+	STAILQ_INSERT_TAIL(&node_list, node, next);
+	graph_spinlock_unlock();
+
+	return node->id;
+free:
+	free(node);
+fail:
+	graph_spinlock_unlock();
+	return RTE_NODE_ID_INVALID;
+}
+
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 55ef35df5..0884c09f1 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -1,3 +1,7 @@
 EXPERIMENTAL {
+	global:
 
+	__rte_node_register;
+
+	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 03/29] graph: implement node operations
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 01/29] graph: define the public API for graph support jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 02/29] graph: implement node registration jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 04/29] graph: implement node debug routines jerinj
                           ` (27 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding node-specific API implementation like cloning node, updating
edges for the node, shrinking edges of a node, retrieving edges of a
node.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph_private.h       |  10 +
 lib/librte_graph/node.c                | 271 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  10 +
 3 files changed, 291 insertions(+)

diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 8b9ff5292..7ed6d01b6 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+
+#define ID_CHECK(id, id_max)                                                   \
+	do {                                                                   \
+		if ((id) >= (id_max)) {                                        \
+			rte_errno = EINVAL;                                    \
+			goto fail;                                             \
+		}                                                              \
+	} while (0)
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 336cd1c94..392e534a9 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -17,6 +17,8 @@
 static struct node_head node_list = STAILQ_HEAD_INITIALIZER(node_list);
 static rte_node_t node_id;
 
+#define NODE_ID_CHECK(id) ID_CHECK(id, node_id)
+
 /* Private functions */
 struct node_head *
 node_list_head_get(void)
@@ -111,3 +113,272 @@ __rte_node_register(const struct rte_node_register *reg)
 	return RTE_NODE_ID_INVALID;
 }
 
+static int
+clone_name(struct rte_node_register *reg, struct node *node, const char *name)
+{
+	ssize_t sz, rc;
+
+#define SZ RTE_NODE_NAMESIZE
+	rc = rte_strscpy(reg->name, node->name, SZ);
+	if (rc < 0)
+		goto fail;
+	sz = rc;
+	rc = rte_strscpy(reg->name + sz, "-", RTE_MAX((int16_t)(SZ - sz), 0));
+	if (rc < 0)
+		goto fail;
+	sz += rc;
+	sz = rte_strscpy(reg->name + sz, name, RTE_MAX((int16_t)(SZ - sz), 0));
+	if (sz < 0)
+		goto fail;
+
+	return 0;
+fail:
+	rte_errno = E2BIG;
+	return -rte_errno;
+}
+
+static rte_node_t
+node_clone(struct node *node, const char *name)
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct rte_node_register *reg;
+	rte_edge_t i;
+
+	/* Don't allow to clone a node from a cloned node */
+	if (node->parent_id != RTE_NODE_ID_INVALID) {
+		rte_errno = EEXIST;
+		goto fail;
+	}
+
+	/* Check for duplicate name */
+	if (node_has_duplicate_entry(name))
+		goto fail;
+
+	reg = calloc(1, sizeof(*reg) + (sizeof(char *) * node->nb_edges));
+	if (reg == NULL) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Clone the source node */
+	reg->flags = node->flags;
+	reg->process = node->process;
+	reg->init = node->init;
+	reg->fini = node->fini;
+	reg->nb_edges = node->nb_edges;
+	reg->parent_id = node->id;
+
+	for (i = 0; i < node->nb_edges; i++)
+		reg->next_nodes[i] = node->next_nodes[i];
+
+	/* Naming ceremony of the new node. name is node->name + "-" + name */
+	if (clone_name(reg, node, name))
+		goto free;
+
+	rc = __rte_node_register(reg);
+free:
+	free(reg);
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_clone(rte_node_t id, const char *name)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node_clone(node, name);
+
+fail:
+	return RTE_NODE_ID_INVALID;
+}
+
+rte_node_t
+rte_node_from_name(const char *name)
+{
+	struct node *node;
+
+	STAILQ_FOREACH(node, &node_list, next)
+		if (strncmp(node->name, name, RTE_NODE_NAMESIZE) == 0)
+			return node->id;
+
+	return RTE_NODE_ID_INVALID;
+}
+
+char *
+rte_node_id_to_name(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->name;
+
+fail:
+	return NULL;
+}
+
+rte_edge_t
+rte_node_edge_count(rte_node_t id)
+{
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	STAILQ_FOREACH(node, &node_list, next)
+		if (node->id == id)
+			return node->nb_edges;
+fail:
+	return RTE_EDGE_ID_INVALID;
+}
+
+static rte_edge_t
+edge_update(struct node *node, struct node *prev, rte_edge_t from,
+	    const char **next_nodes, rte_edge_t nb_edges)
+{
+	rte_edge_t i, max_edges, count = 0;
+	struct node *new_node;
+	bool need_realloc;
+	size_t sz;
+
+	if (from == RTE_EDGE_ID_INVALID)
+		from = node->nb_edges;
+
+	/* Don't create hole in next_nodes[] list */
+	if (from > node->nb_edges) {
+		rte_errno = ENOMEM;
+		goto fail;
+	}
+
+	/* Remove me from list */
+	STAILQ_REMOVE(&node_list, node, node, next);
+
+	/* Allocate the storage space for new node if required */
+	max_edges = from + nb_edges;
+	need_realloc = max_edges > node->nb_edges;
+	if (need_realloc) {
+		sz = sizeof(struct node) + (max_edges * RTE_NODE_NAMESIZE);
+		new_node = realloc(node, sz);
+		if (new_node == NULL) {
+			rte_errno = ENOMEM;
+			goto restore;
+		} else {
+			node = new_node;
+		}
+	}
+
+	/* Update the new nodes name */
+	for (i = from; i < max_edges; i++, count++) {
+		if (rte_strscpy(node->next_nodes[i], next_nodes[count],
+				RTE_NODE_NAMESIZE) < 0) {
+			rte_errno = E2BIG;
+			goto restore;
+		}
+	}
+restore:
+	/* Update the linked list to point new node address in prev node */
+	if (prev)
+		STAILQ_INSERT_AFTER(&node_list, prev, node, next);
+	else
+		STAILQ_INSERT_HEAD(&node_list, node, next);
+
+	if (need_realloc)
+		node->nb_edges = max_edges;
+
+fail:
+	return count;
+}
+
+rte_edge_t
+rte_node_edge_shrink(rte_node_t id, rte_edge_t size)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (node->nb_edges < size) {
+				rte_errno = E2BIG;
+				goto fail;
+			}
+			node->nb_edges = size;
+			rc = size;
+			break;
+		}
+	}
+
+fail:
+	graph_spinlock_unlock();
+	return rc;
+}
+
+rte_edge_t
+rte_node_edge_update(rte_node_t id, rte_edge_t from, const char **next_nodes,
+		     uint16_t nb_edges)
+{
+	rte_edge_t rc = RTE_EDGE_ID_INVALID;
+	struct node *n, *prev;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	prev = NULL;
+	STAILQ_FOREACH(n, &node_list, next) {
+		if (n->id == id) {
+			rc = edge_update(n, prev, from, next_nodes, nb_edges);
+			break;
+		}
+		prev = n;
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+static rte_node_t
+node_copy_edges(struct node *node, char *next_nodes[])
+{
+	rte_edge_t i;
+
+	for (i = 0; i < node->nb_edges; i++)
+		next_nodes[i] = node->next_nodes[i];
+
+	return i;
+}
+
+rte_node_t
+rte_node_edge_get(rte_node_t id, char *next_nodes[])
+{
+	rte_node_t rc = RTE_NODE_ID_INVALID;
+	struct node *node;
+
+	NODE_ID_CHECK(id);
+	graph_spinlock_lock();
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (node->id == id) {
+			if (next_nodes == NULL)
+				rc = sizeof(char *) * node->nb_edges;
+			else
+				rc = node_copy_edges(node, next_nodes);
+			break;
+		}
+	}
+
+	graph_spinlock_unlock();
+fail:
+	return rc;
+}
+
+rte_node_t
+rte_node_max_count(void)
+{
+	return node_id;
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 0884c09f1..412386356 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,5 +3,15 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 
+	rte_node_clone;
+	rte_node_edge_count;
+	rte_node_edge_get;
+	rte_node_edge_shrink;
+	rte_node_edge_update;
+	rte_node_from_name;
+	rte_node_id_to_name;
+	rte_node_list_dump;
+	rte_node_max_count;
+
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 04/29] graph: implement node debug routines
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (2 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 03/29] graph: implement node operations jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 05/29] graph: implement internal graph operation helpers jerinj
                           ` (26 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding node debug API implementation support to dump
single or all the node objects to the given file.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |  1 +
 lib/librte_graph/graph_debug.c         | 25 ++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 12 ++++++++++
 lib/librte_graph/meson.build           |  2 +-
 lib/librte_graph/node.c                | 32 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 6 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_debug.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 933d0ee49..2a6d86933 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
new file mode 100644
index 000000000..75238e7ca
--- /dev/null
+++ b/lib/librte_graph/graph_debug.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_debug.h>
+
+#include "graph_private.h"
+
+void
+node_dump(FILE *f, struct node *n)
+{
+	rte_edge_t i;
+
+	fprintf(f, "node <%s>\n", n->name);
+	fprintf(f, "  id=%" PRIu32 "\n", n->id);
+	fprintf(f, "  flags=0x%" PRIx64 "\n", n->flags);
+	fprintf(f, "  addr=%p\n", n);
+	fprintf(f, "  process=%p\n", n->process);
+	fprintf(f, "  nb_edges=%d\n", n->nb_edges);
+
+	for (i = 0; i < n->nb_edges; i++)
+		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
+}
+
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7ed6d01b6..6db04cee7 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -82,4 +82,16 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/**
+ * @internal
+ *
+ * Dump internal node object data.
+ *
+ * @param f
+ *   FILE pointer to dump the info.
+ * @param g
+ *   Pointer to the internal node object.
+ */
+void node_dump(FILE *f, struct node *n);
+
 #endif /* _RTE_GRAPH_PRIVATE_H_ */
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 5754ac23b..01512182f 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c')
+sources = files('node.c', 'graph.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 392e534a9..0652d40c2 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -377,6 +377,38 @@ rte_node_edge_get(rte_node_t id, char *next_nodes[])
 	return rc;
 }
 
+static void
+node_scan_dump(FILE *f, rte_node_t id, bool all)
+{
+	struct node *node;
+
+	RTE_ASSERT(f != NULL);
+	NODE_ID_CHECK(id);
+
+	STAILQ_FOREACH(node, &node_list, next) {
+		if (all == true) {
+			node_dump(f, node);
+		} else if (node->id == id) {
+			node_dump(f, node);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_node_dump(FILE *f, rte_node_t id)
+{
+	node_scan_dump(f, id, false);
+}
+
+void
+rte_node_list_dump(FILE *f)
+{
+	node_scan_dump(f, 0, true);
+}
+
 rte_node_t
 rte_node_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 412386356..f2c2139c5 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,7 @@ EXPERIMENTAL {
 	__rte_node_register;
 
 	rte_node_clone;
+	rte_node_dump;
 	rte_node_edge_count;
 	rte_node_edge_get;
 	rte_node_edge_shrink;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 05/29] graph: implement internal graph operation helpers
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (3 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 04/29] graph: implement node debug routines jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 06/29] graph: populate fastpath memory for graph reel jerinj
                           ` (25 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding internal graph API helpers support to check whether a graph has
isolated nodes and any node have a loop to itself and BFS
algorithm implementation etc.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile        |   1 +
 lib/librte_graph/graph.c         |   2 +-
 lib/librte_graph/graph_ops.c     | 169 ++++++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h | 173 +++++++++++++++++++++++++++++++
 lib/librte_graph/meson.build     |   2 +-
 5 files changed, 345 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_ops.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 2a6d86933..39ecb2652 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -16,6 +16,7 @@ EXPORT_MAP := rte_graph_version.map
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
 
 # install header files
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index a9c124896..4c3f2fe7b 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -7,7 +7,7 @@
 #include "graph_private.h"
 
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
-
+int rte_graph_logtype;
 void
 graph_spinlock_lock(void)
 {
diff --git a/lib/librte_graph/graph_ops.c b/lib/librte_graph/graph_ops.c
new file mode 100644
index 000000000..335595311
--- /dev/null
+++ b/lib/librte_graph/graph_ops.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+
+#include "graph_private.h"
+
+/* Check whether a node has next_node to itself */
+static inline int
+node_has_loop_edge(struct node *node)
+{
+	rte_edge_t i;
+	char *name;
+	int rc = 0;
+
+	for (i = 0; i < node->nb_edges; i++) {
+		if (strncmp(node->name, node->next_nodes[i],
+			    RTE_NODE_NAMESIZE) == 0) {
+			name = node->name;
+			rc = 1;
+			SET_ERR_JMP(EINVAL, fail, "Node %s has loop to self",
+				    name);
+		}
+	}
+fail:
+	return rc;
+}
+
+int
+graph_node_has_loop_edge(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (node_has_loop_edge(graph_node->node))
+			return 1;
+
+	return 0;
+}
+
+rte_node_t
+graph_src_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t rc = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F)
+			rc++;
+
+	if (rc == 0)
+		SET_ERR_JMP(EINVAL, fail, "Graph needs at least a source node");
+fail:
+	return rc;
+}
+
+/* Check whether a node has next_node to a source node */
+int
+graph_node_has_edge_to_src_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *node;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			node = graph_node->adjacency_list[i]->node;
+			if (node->flags & RTE_NODE_SOURCE_F)
+				SET_ERR_JMP(
+					EEXIST, fail,
+					"Node %s points to the source node %s",
+					graph_node->node->name, node->name);
+		}
+	}
+
+	return 0;
+fail:
+	return 1;
+}
+
+rte_node_t
+graph_nodes_count(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t count = 0;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		count++;
+
+	return count;
+}
+
+void
+graph_mark_nodes_as_not_visited(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		graph_node->visited = false;
+}
+
+int
+graph_bfs(struct graph *graph, struct graph_node *start)
+{
+	struct graph_node **queue, *v, *tmp;
+	uint16_t head = 0, tail = 0;
+	rte_edge_t i;
+	size_t sz;
+
+	sz = sizeof(struct graph_node *) * graph_nodes_count(graph);
+	queue = malloc(sz);
+	if (queue == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to alloc BFS queue of %zu",
+			    sz);
+
+	/* BFS algorithm */
+	queue[tail++] = start;
+	start->visited = true;
+	while (head != tail) {
+		v = queue[head++];
+		for (i = 0; i < v->node->nb_edges; i++) {
+			tmp = v->adjacency_list[i];
+			if (tmp->visited == false) {
+				queue[tail++] = tmp;
+				tmp->visited = true;
+			}
+		}
+	}
+
+	free(queue);
+	return 0;
+
+fail:
+	return -rte_errno;
+}
+
+/* Check whether a node has connected path or parent node */
+int
+graph_has_isolated_node(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	graph_mark_nodes_as_not_visited(graph);
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			if (graph_node->node->nb_edges == 0)
+				SET_ERR_JMP(EINVAL, fail,
+					    "%s node needs minimum one edge",
+					    graph_node->node->name);
+			if (graph_bfs(graph, graph_node))
+				goto fail;
+		}
+	}
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->visited == false)
+			SET_ERR_JMP(EINVAL, fail, "Found isolated node %s",
+				    graph_node->node->name);
+
+	return 0;
+fail:
+	return 1;
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 6db04cee7..220a35e2a 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -13,6 +13,16 @@
 
 #include "rte_graph.h"
 
+extern int rte_graph_logtype;
+
+#define GRAPH_LOG(level, ...)                                                  \
+	rte_log(RTE_LOG_##level, rte_graph_logtype,                            \
+		RTE_FMT("GRAPH: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",    \
+			__func__, __LINE__, RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define graph_err(...) GRAPH_LOG(ERR, __VA_ARGS__)
+#define graph_info(...) GRAPH_LOG(INFO, __VA_ARGS__)
+#define graph_dbg(...) GRAPH_LOG(DEBUG, __VA_ARGS__)
 
 #define ID_CHECK(id, id_max)                                                   \
 	do {                                                                   \
@@ -22,6 +32,12 @@
 		}                                                              \
 	} while (0)
 
+#define SET_ERR_JMP(err, where, fmt, ...)                                      \
+	do {                                                                   \
+		graph_err(fmt, ##__VA_ARGS__);                                 \
+		rte_errno = err;                                               \
+		goto where;                                                    \
+	} while (0)
 
 /**
  * @internal
@@ -41,6 +57,52 @@ struct node {
 	char next_nodes[][RTE_NODE_NAMESIZE]; /**< Names of next nodes. */
 };
 
+/**
+ * @internal
+ *
+ * Structure that holds the graph node data.
+ */
+struct graph_node {
+	STAILQ_ENTRY(graph_node) next; /**< Next graph node in the list. */
+	struct node *node; /**< Pointer to internal node. */
+	bool visited;      /**< Flag used in BFS to mark node visited. */
+	struct graph_node *adjacency_list[]; /**< Adjacency list of the node. */
+};
+
+/**
+ * @internal
+ *
+ * Structure that holds graph internal data.
+ */
+struct graph {
+	STAILQ_ENTRY(graph) next;
+	/**< List of graphs. */
+	char name[RTE_GRAPH_NAMESIZE];
+	/**< Name of the graph. */
+	const struct rte_memzone *mz;
+	/**< Memzone to store graph data. */
+	rte_graph_off_t nodes_start;
+	/**< Node memory start offset in graph reel. */
+	rte_node_t src_node_count;
+	/**< Number of source nodes in a graph. */
+	struct rte_graph *graph;
+	/**< Pointer to graph data. */
+	rte_node_t node_count;
+	/**< Total number of nodes. */
+	uint32_t cir_start;
+	/**< Circular buffer start offset in graph reel. */
+	uint32_t cir_mask;
+	/**< Circular buffer mask for wrap around. */
+	rte_graph_t id;
+	/**< Graph identifier. */
+	size_t mem_sz;
+	/**< Memory size of the graph. */
+	int socket;
+	/**< Socket identifier where memory is allocated. */
+	STAILQ_HEAD(gnode_list, graph_node) node_list;
+	/**< Nodes in a graph. */
+};
+
 /* Node functions */
 STAILQ_HEAD(node_head, node);
 
@@ -67,6 +129,19 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);
 
+/* Graph list functions */
+STAILQ_HEAD(graph_head, graph);
+
+/**
+ * @internal
+ *
+ * Get the head of the graph list.
+ *
+ * @return
+ *   Pointer to the graph head.
+ */
+struct graph_head *graph_list_head_get(void);
+
 /* Lock functions */
 /**
  * @internal
@@ -82,6 +157,104 @@ void graph_spinlock_lock(void);
  */
 void graph_spinlock_unlock(void);
 
+/* Graph operations */
+/**
+ * @internal
+ *
+ * Run a BFS(Breadth First Search) on the graph marking all
+ * the graph nodes as visited.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ * @param start
+ *   Pointer to the starting graph node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough memory for BFS.
+ */
+int graph_bfs(struct graph *graph, struct graph_node *start);
+
+/**
+ * @internal
+ *
+ * Check if there is an isolated node in the given graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: No isolated node found.
+ *   - 1: Isolated node found.
+ */
+int graph_has_isolated_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Check whether a node in the graph has next_node to a source node.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to source node.
+ *   - 1: Node doesn't have an edge to source node.
+ */
+int graph_node_has_edge_to_src_node(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Checks whether node in the graph has a edge to itself i.e. forms a
+ * loop.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Node has an edge to itself.
+ *   - 1: Node doesn't have an edge to itself.
+ */
+int graph_node_has_loop_edge(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of source nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of source nodes.
+ */
+rte_node_t graph_src_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Get the count of total number of nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   Number of nodes.
+ */
+rte_node_t graph_nodes_count(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Clear the visited flag of all the nodes in the graph.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ */
+void graph_mark_nodes_as_not_visited(struct graph *graph);
+
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 01512182f..16e0625c1 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_debug.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
 headers = files('rte_graph.h')
 allow_experimental_apis = true
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 06/29] graph: populate fastpath memory for graph reel
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (4 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 05/29] graph: implement internal graph operation helpers jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 07/29] graph: implement create and destroy APIs jerinj
                           ` (24 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding support to create and populate the memory for graph reel.
This includes reserving the memory in the memzone, populating the nodes,
Allocating memory for node-specific streams to hold objects.

Once it is populated the reel memory contains the following sections.

+---------------------+
|   Graph Header      |
+---------------------+
|   Fence             |
+---------------------+
|   Circular buffer   |
+---------------------+
|   Fence             |
+---------------------+
|   Node Object 0     |
+------------------- -+
|   Node Object 1     |
+------------------- -+
|   Node Object 2     |
+------------------- -+
|   Node Object n     |
+------------------- -+

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   2 +
 lib/librte_graph/graph.c               |  16 ++
 lib/librte_graph/graph_populate.c      | 234 +++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       |  64 +++++++
 lib/librte_graph/meson.build           |   4 +-
 lib/librte_graph/node.c                |   5 +
 lib/librte_graph/rte_graph_version.map |   1 +
 lib/librte_graph/rte_graph_worker.h    | 106 +++++++++++
 8 files changed, 430 insertions(+), 2 deletions(-)
 create mode 100644 lib/librte_graph/graph_populate.c
 create mode 100644 lib/librte_graph/rte_graph_worker.h

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 39ecb2652..7bfd7d51f 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,8 +18,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph.h
+SYMLINK-$(CONFIG_RTE_LIBRTE_GRAPH)-include += rte_graph_worker.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 4c3f2fe7b..e1930b7d2 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,6 +2,7 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <rte_malloc.h>
 #include <rte_spinlock.h>
 
 #include "graph_private.h"
@@ -19,3 +20,18 @@ graph_spinlock_unlock(void)
 {
 	rte_spinlock_unlock(&graph_lock);
 }
+
+void __rte_noinline
+__rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
diff --git a/lib/librte_graph/graph_populate.c b/lib/librte_graph/graph_populate.c
new file mode 100644
index 000000000..093512efa
--- /dev/null
+++ b/lib/librte_graph/graph_populate.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "graph_private.h"
+
+static size_t
+graph_fp_mem_calc_size(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	rte_node_t val;
+	size_t sz;
+
+	/* Graph header */
+	sz = sizeof(struct rte_graph);
+	/* Source nodes list */
+	sz += sizeof(rte_graph_off_t) * graph->src_node_count;
+	/* Circular buffer for pending streams of size number of nodes */
+	val = rte_align32pow2(graph->node_count * sizeof(rte_graph_off_t));
+	sz = RTE_ALIGN(sz, val);
+	graph->cir_start = sz;
+	graph->cir_mask = rte_align32pow2(graph->node_count) - 1;
+	sz += val;
+	/* Fence */
+	sz += sizeof(RTE_GRAPH_FENCE);
+	sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+	graph->nodes_start = sz;
+	/* For 0..N node objects with fence */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		sz = RTE_ALIGN(sz, RTE_CACHE_LINE_SIZE);
+		sz += sizeof(struct rte_node);
+		/* Pointer to next nodes(edges) */
+		sz += sizeof(struct rte_node *) * graph_node->node->nb_edges;
+	}
+
+	graph->mem_sz = sz;
+	return sz;
+}
+
+static void
+graph_header_popluate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+
+	graph->tail = 0;
+	graph->head = (int32_t)-_graph->src_node_count;
+	graph->cir_mask = _graph->cir_mask;
+	graph->nb_nodes = _graph->node_count;
+	graph->cir_start = RTE_PTR_ADD(graph, _graph->cir_start);
+	graph->nodes_start = _graph->nodes_start;
+	graph->socket = _graph->socket;
+	graph->id = _graph->id;
+	memcpy(graph->name, _graph->name, RTE_GRAPH_NAMESIZE);
+	graph->fence = RTE_GRAPH_FENCE;
+}
+
+static void
+graph_nodes_populate(struct graph *_graph)
+{
+	rte_graph_off_t off = _graph->nodes_start;
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	rte_edge_t count, nb_edges;
+	const char *parent;
+	rte_node_t pid;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		struct rte_node *node = RTE_PTR_ADD(graph, off);
+		memset(node, 0, sizeof(*node));
+		node->fence = RTE_GRAPH_FENCE;
+		node->off = off;
+		node->process = graph_node->node->process;
+		memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
+		pid = graph_node->node->parent_id;
+		if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
+			parent = rte_node_id_to_name(pid);
+			memcpy(node->parent, parent, RTE_GRAPH_NAMESIZE);
+		}
+		node->id = graph_node->node->id;
+		node->parent_id = pid;
+		nb_edges = graph_node->node->nb_edges;
+		node->nb_edges = nb_edges;
+		off += sizeof(struct rte_node);
+		/* Copy the name in first pass to replace with rte_node* later*/
+		for (count = 0; count < nb_edges; count++)
+			node->nodes[count] = (struct rte_node *)&graph_node
+						     ->adjacency_list[count]
+						     ->node->name[0];
+
+		off += sizeof(struct rte_node *) * nb_edges;
+		off = RTE_ALIGN(off, RTE_CACHE_LINE_SIZE);
+		node->next = off;
+		__rte_node_stream_alloc(graph, node);
+	}
+}
+
+struct rte_node *
+graph_node_id_to_ptr(const struct rte_graph *graph, rte_node_t id)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (unlikely(node->id == id))
+			return node;
+
+	return NULL;
+}
+
+struct rte_node *
+graph_node_name_to_ptr(const struct rte_graph *graph, const char *name)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		if (strncmp(name, node->name, RTE_NODE_NAMESIZE) == 0)
+			return node;
+
+	return NULL;
+}
+
+static int
+graph_node_nexts_populate(struct graph *_graph)
+{
+	rte_node_t count, val;
+	rte_graph_off_t off;
+	struct rte_node *node;
+	const struct rte_graph *graph = _graph->graph;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		for (val = 0; val < node->nb_edges; val++) {
+			name = (const char *)node->nodes[val];
+			node->nodes[val] = graph_node_name_to_ptr(graph, name);
+			if (node->nodes[val] == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_src_nodes_populate(struct graph *_graph)
+{
+	struct rte_graph *graph = _graph->graph;
+	struct graph_node *graph_node;
+	struct rte_node *node;
+	int32_t head = -1;
+	const char *name;
+
+	STAILQ_FOREACH(graph_node, &_graph->node_list, next) {
+		if (graph_node->node->flags & RTE_NODE_SOURCE_F) {
+			name = graph_node->node->name;
+			node = graph_node_name_to_ptr(graph, name);
+			if (node == NULL)
+				SET_ERR_JMP(EINVAL, fail, "%s not found", name);
+
+			__rte_node_stream_alloc(graph, node);
+			graph->cir_start[head--] = node->off;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_fp_mem_populate(struct graph *graph)
+{
+	int rc;
+
+	graph_header_popluate(graph);
+	graph_nodes_populate(graph);
+	rc = graph_node_nexts_populate(graph);
+	rc |= graph_src_nodes_populate(graph);
+
+	return rc;
+}
+
+int
+graph_fp_mem_create(struct graph *graph)
+{
+	const struct rte_memzone *mz;
+	size_t sz;
+
+	sz = graph_fp_mem_calc_size(graph);
+	mz = rte_memzone_reserve(graph->name, sz, graph->socket, 0);
+	if (mz == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Memzone %s reserve failed",
+			    graph->name);
+
+	graph->graph = mz->addr;
+	graph->mz = mz;
+
+	return graph_fp_mem_populate(graph);
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_nodes_mem_destroy(struct rte_graph *graph)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *node;
+
+	if (graph == NULL)
+		return;
+
+	rte_graph_foreach_node(count, off, graph, node)
+		rte_free(node->objs);
+}
+
+int
+graph_fp_mem_destroy(struct graph *graph)
+{
+	graph_nodes_mem_destroy(graph->graph);
+	return rte_memzone_free(graph->mz);
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 220a35e2a..7fce52e00 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -12,6 +12,7 @@
 #include <rte_eal.h>
 
 #include "rte_graph.h"
+#include "rte_graph_worker.h"
 
 extern int rte_graph_logtype;
 
@@ -254,6 +255,69 @@ rte_node_t graph_nodes_count(struct graph *graph);
  */
 void graph_mark_nodes_as_not_visited(struct graph *graph);
 
+/* Fast path graph memory populate unctions */
+
+/**
+ * @internal
+ *
+ * Create fast-path memory for the graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOMEM: Not enough for graph and nodes.
+ *   - -EINVAL: Graph nodes not found.
+ */
+int graph_fp_mem_create(struct graph *graph);
+
+/**
+ * @internal
+ *
+ * Free fast-path memory used by graph and nodes.
+ *
+ * @param graph
+ *   Pointer to the internal graph object.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Graph memzone related error.
+ */
+int graph_fp_mem_destroy(struct graph *graph);
+
+/* Lookup functions */
+/**
+ * @internal
+ *
+ * Get graph node object from node id.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param id
+ *   Node Identifier.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
+				      rte_node_t id);
+
+/**
+ * @internal
+ *
+ * Get graph node object from node name.
+ *
+ * @param graph
+ *   Pointer to rte_graph object.
+ * @param node_name
+ *   Pointer to character string holding the node name.
+ *
+ * @return
+ *   Pointer to rte_node if identifier is valid else NULL.
+ */
+struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
+					const char *node_name);
 
 /**
  * @internal
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index 16e0625c1..fb203a5e2 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,8 +4,8 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c')
-headers = files('rte_graph.h')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
 deps += ['eal']
diff --git a/lib/librte_graph/node.c b/lib/librte_graph/node.c
index 0652d40c2..873c9ab16 100644
--- a/lib/librte_graph/node.c
+++ b/lib/librte_graph/node.c
@@ -61,6 +61,11 @@ __rte_node_register(const struct rte_node_register *reg)
 	rte_edge_t i;
 	size_t sz;
 
+	/* Limit Node specific metadata to one cacheline on 64B CL machine */
+	RTE_BUILD_BUG_ON((offsetof(struct rte_node, nodes) -
+			  offsetof(struct rte_node, ctx)) !=
+			 RTE_CACHE_LINE_MIN_SIZE);
+
 	graph_spinlock_lock();
 
 	/* Check sanity */
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index f2c2139c5..a9fe1b610 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -2,6 +2,7 @@ EXPERIMENTAL {
 	global:
 
 	__rte_node_register;
+	__rte_node_stream_alloc;
 
 	rte_node_clone;
 	rte_node_dump;
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
new file mode 100644
index 000000000..42be8fd13
--- /dev/null
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_WORKER_H_
+#define _RTE_GRAPH_WORKER_H_
+
+/**
+ * @file rte_graph_worker.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows a worker thread to walk over a graph and nodes to create,
+ * process, enqueue and move streams of objects to the next nodes.
+ */
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_prefetch.h>
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+
+#include "rte_graph.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ *
+ * Data structure to hold graph data.
+ */
+struct rte_graph {
+	uint32_t tail;		     /**< Tail of circular buffer. */
+	uint32_t head;		     /**< Head of circular buffer. */
+	uint32_t cir_mask;	     /**< Circular buffer wrap around mask. */
+	rte_node_t nb_nodes;	     /**< Number of nodes in the graph. */
+	rte_graph_off_t *cir_start;  /**< Pointer to circular buffer. */
+	rte_graph_off_t nodes_start; /**< Offset at which node memory starts. */
+	rte_graph_t id;	/**< Graph identifier. */
+	int socket;	/**< Socket ID where memory is allocated. */
+	char name[RTE_GRAPH_NAMESIZE];	/**< Name of the graph. */
+	uint64_t fence;			/**< Fence. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Data structure to hold node data.
+ */
+struct rte_node {
+	/* Slow path area  */
+	uint64_t fence;		/**< Fence. */
+	rte_graph_off_t next;	/**< Index to next node. */
+	rte_node_t id;		/**< Node identifier. */
+	rte_node_t parent_id;	/**< Parent Node identifier. */
+	rte_edge_t nb_edges;	/**< Number of edges from this node. */
+	uint32_t realloc_count;	/**< Number of times realloced. */
+
+	char parent[RTE_NODE_NAMESIZE];	/**< Parent node name. */
+	char name[RTE_NODE_NAMESIZE];	/**< Name of the node. */
+
+	/* Fast path area  */
+#define RTE_NODE_CTX_SZ 16
+	uint8_t ctx[RTE_NODE_CTX_SZ] __rte_cache_aligned; /**< Node Context. */
+	uint16_t size;		/**< Total number of objects available. */
+	uint16_t idx;		/**< Number of objects used. */
+	rte_graph_off_t off;	/**< Offset of node in the graph reel. */
+	uint64_t total_cycles;	/**< Cycles spent in this node. */
+	uint64_t total_calls;	/**< Calls done to this node. */
+	uint64_t total_objs;	/**< Objects processed by this node. */
+	RTE_STD_C11
+		union {
+			void **objs;	   /**< Array of object pointers. */
+			uint64_t objs_u64;
+		};
+	RTE_STD_C11
+		union {
+			rte_node_process_t process; /**< Process function. */
+			uint64_t process_u64;
+		};
+	struct rte_node *nodes[] __rte_cache_min_aligned; /**< Next nodes. */
+} __rte_cache_aligned;
+
+/**
+ * @internal
+ *
+ * Allocate a stream of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ */
+__rte_experimental
+void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GRAPH_WORKER_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 07/29] graph: implement create and destroy APIs
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (5 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 06/29] graph: populate fastpath memory for graph reel jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 08/29] graph: implement graph operation APIs jerinj
                           ` (23 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding graph specific API implementations like graph create
and graph destroy. This detect loops in the graph,
check for isolated nodes and operation to verify the validity of
graph.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 319 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   2 +
 2 files changed, 321 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e1930b7d2..f4e02fdfc 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -2,13 +2,33 @@
  * Copyright(C) 2020 Marvell International Ltd.
  */
 
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_errno.h>
 #include <rte_malloc.h>
+#include <rte_memzone.h>
 #include <rte_spinlock.h>
+#include <rte_string_fns.h>
 
 #include "graph_private.h"
 
+static struct graph_head graph_list = STAILQ_HEAD_INITIALIZER(graph_list);
 static rte_spinlock_t graph_lock = RTE_SPINLOCK_INITIALIZER;
+static rte_graph_t graph_id;
 int rte_graph_logtype;
+
+#define GRAPH_ID_CHECK(id) ID_CHECK(id, graph_id)
+
+/* Private functions */
+struct graph_head *
+graph_list_head_get(void)
+{
+	return &graph_list;
+}
+
 void
 graph_spinlock_lock(void)
 {
@@ -21,6 +41,305 @@ graph_spinlock_unlock(void)
 	rte_spinlock_unlock(&graph_lock);
 }
 
+static int
+graph_node_add(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+	size_t sz;
+
+	/* Skip the duplicate nodes */
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (strncmp(node->name, graph_node->node->name,
+			    RTE_NODE_NAMESIZE) == 0)
+			return 0;
+
+	/* Allocate new graph node object */
+	sz = sizeof(*graph_node) + node->nb_edges * sizeof(struct node *);
+	graph_node = calloc(1, sz);
+
+	if (graph_node == NULL)
+		SET_ERR_JMP(ENOMEM, free, "Failed to calloc %s object",
+			    node->name);
+
+	/* Initialize the graph node */
+	graph_node->node = node;
+
+	/* Add to graph node list */
+	STAILQ_INSERT_TAIL(&graph->node_list, graph_node, next);
+	return 0;
+
+free:
+	free(graph_node);
+	return -rte_errno;
+}
+
+static struct graph_node *
+node_to_graph_node(struct graph *graph, struct node *node)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node == node)
+			return graph_node;
+
+	SET_ERR_JMP(ENODEV, fail, "Found isolated node %s", node->name);
+fail:
+	return NULL;
+}
+
+static int
+graph_node_edges_add(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			if (graph_node_add(graph, adjacency))
+				goto fail;
+		}
+	}
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+graph_adjacency_list_update(struct graph *graph)
+{
+	struct graph_node *graph_node, *tmp;
+	struct node *adjacency;
+	const char *next;
+	rte_edge_t i;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			next = graph_node->node->next_nodes[i];
+			adjacency = node_from_name(next);
+			if (adjacency == NULL)
+				SET_ERR_JMP(EINVAL, fail,
+					    "Node %s not registered", next);
+			tmp = node_to_graph_node(graph, adjacency);
+			if (tmp == NULL)
+				goto fail;
+			graph_node->adjacency_list[i] = tmp;
+		}
+	}
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static int
+expand_pattern_to_node(struct graph *graph, const char *pattern)
+{
+	struct node_head *node_head = node_list_head_get();
+	bool found = false;
+	struct node *node;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(node, node_head, next) {
+		if (fnmatch(pattern, node->name, 0) == 0) {
+			if (graph_node_add(graph, node))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s node not found", pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+static void
+graph_cleanup(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	while (!STAILQ_EMPTY(&graph->node_list)) {
+		graph_node = STAILQ_FIRST(&graph->node_list);
+		STAILQ_REMOVE_HEAD(&graph->node_list, next);
+		free(graph_node);
+	}
+}
+
+static int
+graph_node_init(struct graph *graph)
+{
+	struct graph_node *graph_node;
+	const char *name;
+	int rc;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		if (graph_node->node->init) {
+			name = graph_node->node->name;
+			rc = graph_node->node->init(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph, name));
+			if (rc)
+				SET_ERR_JMP(rc, err, "Node %s init() failed",
+					    name);
+		}
+	}
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+graph_node_fini(struct graph *graph)
+{
+	struct graph_node *graph_node;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next)
+		if (graph_node->node->fini)
+			graph_node->node->fini(
+				graph->graph,
+				graph_node_name_to_ptr(graph->graph,
+						       graph_node->node->name));
+}
+
+rte_graph_t
+rte_graph_create(const char *name, struct rte_graph_param *prm)
+{
+	rte_node_t src_node_count;
+	struct graph *graph;
+	const char *pattern;
+	uint16_t i;
+
+	graph_spinlock_lock();
+
+	/* Check arguments sanity */
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Param should not be NULL");
+
+	if (name == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Graph name should not be NULL");
+
+	/* Check for existence of duplicate graph */
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(name, graph->name, RTE_GRAPH_NAMESIZE) == 0)
+			SET_ERR_JMP(EEXIST, fail, "Found duplicate graph %s",
+				    name);
+
+	/* Create graph object */
+	graph = calloc(1, sizeof(*graph));
+	if (graph == NULL)
+		SET_ERR_JMP(ENOMEM, fail, "Failed to calloc graph object");
+
+	/* Initialize the graph object */
+	STAILQ_INIT(&graph->node_list);
+	if (rte_strscpy(graph->name, name, RTE_GRAPH_NAMESIZE) < 0)
+		SET_ERR_JMP(E2BIG, free, "Too big name=%s", name);
+
+	/* Expand node pattern and add the nodes to the graph */
+	for (i = 0; i < prm->nb_node_patterns; i++) {
+		pattern = prm->node_patterns[i];
+		if (expand_pattern_to_node(graph, pattern))
+			goto graph_cleanup;
+	}
+
+	/* Go over all the nodes edges and add them to the graph */
+	if (graph_node_edges_add(graph))
+		goto graph_cleanup;
+
+	/* Update adjacency list of all nodes in the graph */
+	if (graph_adjacency_list_update(graph))
+		goto graph_cleanup;
+
+	/* Make sure at least a source node present in the graph */
+	src_node_count = graph_src_nodes_count(graph);
+	if (src_node_count == 0)
+		goto graph_cleanup;
+
+	/* Make sure no node is pointing to source node */
+	if (graph_node_has_edge_to_src_node(graph))
+		goto graph_cleanup;
+
+	/* Don't allow node has loop to self */
+	if (graph_node_has_loop_edge(graph))
+		goto graph_cleanup;
+
+	/* Do BFS from src nodes on the graph to find isolated nodes */
+	if (graph_has_isolated_node(graph))
+		goto graph_cleanup;
+
+	/* Initialize graph object */
+	graph->socket = prm->socket_id;
+	graph->src_node_count = src_node_count;
+	graph->node_count = graph_nodes_count(graph);
+	graph->id = graph_id;
+
+	/* Allocate the Graph fast path memory and populate the data */
+	if (graph_fp_mem_create(graph))
+		goto graph_cleanup;
+
+	/* Call init() of the all the nodes in the graph */
+	if (graph_node_init(graph))
+		goto graph_mem_destroy;
+
+	/* All good, Lets add the graph to the list */
+	graph_id++;
+	STAILQ_INSERT_TAIL(&graph_list, graph, next);
+
+	graph_spinlock_unlock();
+	return graph->id;
+
+graph_mem_destroy:
+	graph_fp_mem_destroy(graph);
+graph_cleanup:
+	graph_cleanup(graph);
+free:
+	free(graph);
+fail:
+	graph_spinlock_unlock();
+	return RTE_GRAPH_ID_INVALID;
+}
+
+int
+rte_graph_destroy(rte_graph_t id)
+{
+	struct graph *graph, *tmp;
+	int rc = -ENOENT;
+
+	graph_spinlock_lock();
+
+	graph = STAILQ_FIRST(&graph_list);
+	while (graph != NULL) {
+		tmp = STAILQ_NEXT(graph, next);
+		if (graph->id == id) {
+			/* Call fini() of the all the nodes in the graph */
+			graph_node_fini(graph);
+			/* Destroy graph fast path memory */
+			rc = graph_fp_mem_destroy(graph);
+			if (rc)
+				SET_ERR_JMP(rc, done, "Graph %s destroy failed",
+					    graph->name);
+
+			graph_cleanup(graph);
+			STAILQ_REMOVE(&graph_list, graph, graph, next);
+			free(graph);
+			graph_id--;
+			goto done;
+		}
+		graph = tmp;
+	}
+done:
+	graph_spinlock_unlock();
+	return rc;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index a9fe1b610..dcbd78c02 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -4,6 +4,8 @@ EXPERIMENTAL {
 	__rte_node_register;
 	__rte_node_stream_alloc;
 
+	rte_graph_create;
+	rte_graph_destroy;
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 08/29] graph: implement graph operation APIs
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (6 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 07/29] graph: implement create and destroy APIs jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 09/29] graph: implement Graphviz export jerinj
                           ` (22 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K, Anatoly Burakov
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding support for graph specific API implementation like
Graph lookup to get graph object, retrieving graph ID
From name and graph name from ID.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 131 +++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |   8 ++
 2 files changed, 139 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index f4e02fdfc..7a77cb5c8 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -210,6 +210,54 @@ graph_node_fini(struct graph *graph)
 						       graph_node->node->name));
 }
 
+static struct rte_graph *
+graph_mem_fixup_node_ctx(struct rte_graph *graph)
+{
+	struct rte_node *node;
+	struct node *node_db;
+	rte_graph_off_t off;
+	rte_node_t count;
+	const char *name;
+
+	rte_graph_foreach_node(count, off, graph, node) {
+		if (node->parent_id == RTE_NODE_ID_INVALID) /* Static node */
+			name = node->name;
+		else /* Cloned node */
+			name = node->parent;
+
+		node_db = node_from_name(name);
+		if (node_db == NULL)
+			SET_ERR_JMP(ENOLINK, fail, "Node %s not found", name);
+		node->process = node_db->process;
+	}
+
+	return graph;
+fail:
+	return NULL;
+}
+
+static struct rte_graph *
+graph_mem_fixup_secondary(struct rte_graph *graph)
+{
+	if (graph == NULL || rte_eal_process_type() == RTE_PROC_PRIMARY)
+		return graph;
+
+	return graph_mem_fixup_node_ctx(graph);
+}
+
+struct rte_graph *
+rte_graph_lookup(const char *name)
+{
+	const struct rte_memzone *mz;
+	struct rte_graph *rc = NULL;
+
+	mz = rte_memzone_lookup(name);
+	if (mz)
+		rc = mz->addr;
+
+	return graph_mem_fixup_secondary(rc);
+}
+
 rte_graph_t
 rte_graph_create(const char *name, struct rte_graph_param *prm)
 {
@@ -340,6 +388,76 @@ rte_graph_destroy(rte_graph_t id)
 	return rc;
 }
 
+rte_graph_t
+rte_graph_from_name(const char *name)
+{
+	struct graph *graph;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0)
+			return graph->id;
+
+	return RTE_GRAPH_ID_INVALID;
+}
+
+char *
+rte_graph_id_to_name(rte_graph_t id)
+{
+	struct graph *graph;
+
+	GRAPH_ID_CHECK(id);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == id)
+			return graph->name;
+
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get(rte_graph_t gid, uint32_t nid)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	GRAPH_ID_CHECK(gid);
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (graph->id == gid) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (node->id == nid)
+					return node;
+			}
+			break;
+		}
+fail:
+	return NULL;
+}
+
+struct rte_node *
+rte_graph_node_get_by_name(const char *graph_name, const char *node_name)
+{
+	struct rte_node *node;
+	struct graph *graph;
+	rte_graph_off_t off;
+	rte_node_t count;
+
+	STAILQ_FOREACH(graph, &graph_list, next)
+		if (!strncmp(graph->name, graph_name, RTE_GRAPH_NAMESIZE)) {
+			rte_graph_foreach_node(count, off, graph->graph,
+						node) {
+				if (!strncmp(node->name, node_name,
+					     RTE_NODE_NAMESIZE))
+					return node;
+			}
+			break;
+		}
+
+	return NULL;
+}
+
 void __rte_noinline
 __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 {
@@ -354,3 +472,16 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->size = size;
 	node->realloc_count++;
 }
+
+rte_graph_t
+rte_graph_max_count(void)
+{
+	return graph_id;
+}
+
+RTE_INIT(rte_graph_init_log)
+{
+	rte_graph_logtype = rte_log_register("lib.graph");
+	if (rte_graph_logtype >= 0)
+		rte_log_set_level(rte_graph_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index dcbd78c02..5a2b13293 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,14 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_from_name;
+	rte_graph_id_to_name;
+	rte_graph_lookup;
+	rte_graph_list_dump;
+	rte_graph_max_count;
+	rte_graph_node_get;
+	rte_graph_node_get_by_name;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 09/29] graph: implement Graphviz export
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (7 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 08/29] graph: implement graph operation APIs jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 10/29] graph: implement debug routines jerinj
                           ` (21 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding API implementation support exporting the graph object to file.
This will export the graph to a file in Graphviz format.
It can be viewed in many viewers such as
https://dreampuf.github.io/GraphvizOnline/

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 53 ++++++++++++++++++++++++++
 lib/librte_graph/rte_graph_version.map |  1 +
 2 files changed, 54 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 7a77cb5c8..61e212e03 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -473,6 +473,59 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+static int
+graph_to_dot(FILE *f, struct graph *graph)
+{
+	const char *src_edge_color = " [color=blue]\n";
+	const char *edge_color = "\n";
+	struct graph_node *graph_node;
+	char *node_name;
+	rte_edge_t i;
+	int rc;
+
+	rc = fprintf(f, "Digraph %s {\n\trankdir=LR;\n", graph->name);
+	if (rc < 0)
+		goto end;
+
+	STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+		node_name = graph_node->node->name;
+		for (i = 0; i < graph_node->node->nb_edges; i++) {
+			rc = fprintf(f, "\t\"%s\"->\"%s\"%s", node_name,
+				     graph_node->adjacency_list[i]->node->name,
+				     graph_node->node->flags & RTE_NODE_SOURCE_F
+					     ? src_edge_color
+					     : edge_color);
+			if (rc < 0)
+				goto end;
+		}
+	}
+	rc = fprintf(f, "}\n");
+	if (rc < 0)
+		goto end;
+
+	return 0;
+end:
+	rte_errno = EBADF;
+	return -rte_errno;
+}
+
+int
+rte_graph_export(const char *name, FILE *f)
+{
+	struct graph *graph;
+	int rc = ENOENT;
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (strncmp(graph->name, name, RTE_GRAPH_NAMESIZE) == 0) {
+			rc = graph_to_dot(f, graph);
+			goto end;
+		}
+	}
+end:
+	return -rc;
+}
+
+
 rte_graph_t
 rte_graph_max_count(void)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 5a2b13293..2797be044 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
 	rte_graph_lookup;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 10/29] graph: implement debug routines
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (8 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 09/29] graph: implement Graphviz export jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 11/29] graph: implement stats support jerinj
                           ` (20 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph specific API to dump the
Graph information to a file. This API will dump detailed internal
info about node objects and graph objects.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/graph.c               | 31 ++++++++++++++
 lib/librte_graph/graph_debug.c         | 59 ++++++++++++++++++++++++++
 lib/librte_graph/graph_private.h       | 13 ++++++
 lib/librte_graph/rte_graph_version.map |  2 +
 4 files changed, 105 insertions(+)

diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index 61e212e03..e811a7b38 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -525,6 +525,37 @@ rte_graph_export(const char *name, FILE *f)
 	return -rc;
 }
 
+static void
+graph_scan_dump(FILE *f, rte_graph_t id, bool all)
+{
+	struct graph *graph;
+
+	RTE_VERIFY(f);
+	GRAPH_ID_CHECK(id);
+
+	STAILQ_FOREACH(graph, &graph_list, next) {
+		if (all == true) {
+			graph_dump(f, graph);
+		} else if (graph->id == id) {
+			graph_dump(f, graph);
+			return;
+		}
+	}
+fail:
+	return;
+}
+
+void
+rte_graph_dump(FILE *f, rte_graph_t id)
+{
+	graph_scan_dump(f, id, false);
+}
+
+void
+rte_graph_list_dump(FILE *f)
+{
+	graph_scan_dump(f, 0, true);
+}
 
 rte_graph_t
 rte_graph_max_count(void)
diff --git a/lib/librte_graph/graph_debug.c b/lib/librte_graph/graph_debug.c
index 75238e7ca..f8aea16ac 100644
--- a/lib/librte_graph/graph_debug.c
+++ b/lib/librte_graph/graph_debug.c
@@ -7,6 +7,26 @@
 
 #include "graph_private.h"
 
+void
+graph_dump(FILE *f, struct graph *g)
+{
+	struct graph_node *graph_node;
+	rte_edge_t i = 0;
+
+	fprintf(f, "graph <%s>\n", g->name);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  cir_start=%" PRIu32 "\n", g->cir_start);
+	fprintf(f, "  cir_mask=%" PRIu32 "\n", g->cir_mask);
+	fprintf(f, "  addr=%p\n", g);
+	fprintf(f, "  graph=%p\n", g->graph);
+	fprintf(f, "  mem_sz=%zu\n", g->mem_sz);
+	fprintf(f, "  node_count=%" PRIu32 "\n", g->node_count);
+	fprintf(f, "  src_node_count=%" PRIu32 "\n", g->src_node_count);
+
+	STAILQ_FOREACH(graph_node, &g->node_list, next)
+		fprintf(f, "     node[%d] <%s>\n", i++, graph_node->node->name);
+}
+
 void
 node_dump(FILE *f, struct node *n)
 {
@@ -23,3 +43,42 @@ node_dump(FILE *f, struct node *n)
 		fprintf(f, "     edge[%d] <%s>\n", i, n->next_nodes[i]);
 }
 
+void
+rte_graph_obj_dump(FILE *f, struct rte_graph *g, bool all)
+{
+	rte_node_t count;
+	rte_graph_off_t off;
+	struct rte_node *n;
+	rte_edge_t i;
+
+	fprintf(f, "graph <%s> @ %p\n", g->name, g);
+	fprintf(f, "  id=%" PRIu32 "\n", g->id);
+	fprintf(f, "  head=%" PRId32 "\n", (int32_t)g->head);
+	fprintf(f, "  tail=%" PRId32 "\n", (int32_t)g->tail);
+	fprintf(f, "  cir_mask=0x%" PRIx32 "\n", g->cir_mask);
+	fprintf(f, "  nb_nodes=%" PRId32 "\n", g->nb_nodes);
+	fprintf(f, "  socket=%d\n", g->socket);
+	fprintf(f, "  fence=0x%" PRIx64 "\n", g->fence);
+	fprintf(f, "  nodes_start=0x%" PRIx32 "\n", g->nodes_start);
+	fprintf(f, "  cir_start=%p\n", g->cir_start);
+
+	rte_graph_foreach_node(count, off, g, n) {
+		if (!all && n->idx == 0)
+			continue;
+		fprintf(f, "     node[%d] <%s>\n", count, n->name);
+		fprintf(f, "       fence=0x%" PRIx64 "\n", n->fence);
+		fprintf(f, "       objs=%p\n", n->objs);
+		fprintf(f, "       process=%p\n", n->process);
+		fprintf(f, "       id=0x%" PRIx32 "\n", n->id);
+		fprintf(f, "       offset=0x%" PRIx32 "\n", n->off);
+		fprintf(f, "       nb_edges=%" PRId32 "\n", n->nb_edges);
+		fprintf(f, "       realloc_count=%d\n", n->realloc_count);
+		fprintf(f, "       size=%d\n", n->size);
+		fprintf(f, "       idx=%d\n", n->idx);
+		fprintf(f, "       total_objs=%" PRId64 "\n", n->total_objs);
+		fprintf(f, "       total_calls=%" PRId64 "\n", n->total_calls);
+		for (i = 0; i < n->nb_edges; i++)
+			fprintf(f, "          edge[%d] <%s>\n", i,
+				n->nodes[i]->name);
+	}
+}
diff --git a/lib/librte_graph/graph_private.h b/lib/librte_graph/graph_private.h
index 7fce52e00..f9a85c892 100644
--- a/lib/librte_graph/graph_private.h
+++ b/lib/librte_graph/graph_private.h
@@ -319,6 +319,19 @@ struct rte_node *graph_node_id_to_ptr(const struct rte_graph *graph,
 struct rte_node *graph_node_name_to_ptr(const struct rte_graph *graph,
 					const char *node_name);
 
+/* Debug functions */
+/**
+ * @internal
+ *
+ * Dump internal graph object data.
+ *
+ * @param f
+ *   FILE pointer to dump the data.
+ * @param g
+ *   Pointer to the internal graph object.
+ */
+void graph_dump(FILE *f, struct graph *g);
+
 /**
  * @internal
  *
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 2797be044..851f4772e 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -6,6 +6,7 @@ EXPERIMENTAL {
 
 	rte_graph_create;
 	rte_graph_destroy;
+	rte_graph_dump;
 	rte_graph_export;
 	rte_graph_from_name;
 	rte_graph_id_to_name;
@@ -14,6 +15,7 @@ EXPERIMENTAL {
 	rte_graph_max_count;
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
+	rte_graph_obj_dump;
 
 	rte_node_clone;
 	rte_node_dump;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 11/29] graph: implement stats support
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (9 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 10/29] graph: implement debug routines jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 12/29] graph: implement fastpath API routines jerinj
                           ` (19 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for graph stats collection API. This API will
create a cluster for a specified node pattern and aggregate the node
runtime stats.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_graph/Makefile              |   1 +
 lib/librte_graph/graph_stats.c         | 406 +++++++++++++++++++++++++
 lib/librte_graph/meson.build           |   2 +-
 lib/librte_graph/rte_graph_version.map |   5 +
 4 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_graph/graph_stats.c

diff --git a/lib/librte_graph/Makefile b/lib/librte_graph/Makefile
index 7bfd7d51f..967c8d9bc 100644
--- a/lib/librte_graph/Makefile
+++ b/lib/librte_graph/Makefile
@@ -18,6 +18,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += node.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_ops.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_debug.c
+SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_stats.c
 SRCS-$(CONFIG_RTE_LIBRTE_GRAPH) += graph_populate.c
 
 # install header files
diff --git a/lib/librte_graph/graph_stats.c b/lib/librte_graph/graph_stats.c
new file mode 100644
index 000000000..125e08d73
--- /dev/null
+++ b/lib/librte_graph/graph_stats.c
@@ -0,0 +1,406 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <fnmatch.h>
+#include <stdbool.h>
+
+#include <rte_common.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+
+#include "graph_private.h"
+
+/* Capture all graphs of cluster */
+struct cluster {
+	rte_graph_t nb_graphs;
+	rte_graph_t size;
+
+	struct graph **graphs;
+};
+
+/* Capture same node ID across cluster  */
+struct cluster_node {
+	struct rte_graph_cluster_node_stats stat;
+	rte_node_t nb_nodes;
+
+	struct rte_node *nodes[];
+};
+
+struct rte_graph_cluster_stats {
+	/* Header */
+	rte_graph_cluster_stats_cb_t fn;
+	uint32_t cluster_node_size; /* Size of struct cluster_node */
+	rte_node_t max_nodes;
+	int socket_id;
+	void *cookie;
+	size_t sz;
+
+	struct cluster_node clusters[];
+} __rte_cache_aligned;
+
+#define boarder()                                                              \
+	fprintf(f, "+-------------------------------+---------------+--------" \
+		   "-------+---------------+---------------+---------------+-" \
+		   "----------+\n")
+
+static inline void
+print_banner(FILE *f)
+{
+	boarder();
+	fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
+		"|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
+		"|cycles/call|");
+	boarder();
+}
+
+static inline void
+print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
+{
+	double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
+	const uint64_t prev_calls = stat->prev_calls;
+	const uint64_t prev_objs = stat->prev_objs;
+	const uint64_t cycles = stat->cycles;
+	const uint64_t calls = stat->calls;
+	const uint64_t objs = stat->objs;
+	uint64_t call_delta;
+
+	call_delta = calls - prev_calls;
+	objs_per_call =
+		call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
+	cycles_per_call =
+		call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
+			   : 0;
+	ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
+	objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
+	objs_per_sec /= 1000000;
+
+	fprintf(f,
+		"|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
+		"|%-15.3f|%-15.6f|%-11.4f|\n",
+		stat->name, calls, objs, stat->realloc_count, objs_per_call,
+		objs_per_sec, cycles_per_call);
+}
+
+static int
+graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
+		       const struct rte_graph_cluster_node_stats *stat)
+{
+	FILE *f = cookie;
+
+	if (unlikely(is_first))
+		print_banner(f);
+	if (stat->objs)
+		print_node(f, stat);
+	if (unlikely(is_last))
+		boarder();
+
+	return 0;
+};
+
+static struct rte_graph_cluster_stats *
+stats_mem_init(struct cluster *cluster,
+	       const struct rte_graph_cluster_stats_param *prm)
+{
+	size_t sz = sizeof(struct rte_graph_cluster_stats);
+	struct rte_graph_cluster_stats *stats;
+	rte_graph_cluster_stats_cb_t fn;
+	int socket_id = prm->socket_id;
+	uint32_t cluster_node_size;
+
+	/* Fix up callback */
+	fn = prm->fn;
+	if (fn == NULL)
+		fn = graph_cluster_stats_cb;
+
+	cluster_node_size = sizeof(struct cluster_node);
+	/* For a given cluster, max nodes will be the max number of graphs */
+	cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
+	cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
+
+	stats = realloc(NULL, sz);
+	memset(stats, 0, sz);
+	if (stats) {
+		stats->fn = fn;
+		stats->cluster_node_size = cluster_node_size;
+		stats->max_nodes = 0;
+		stats->socket_id = socket_id;
+		stats->cookie = prm->cookie;
+		stats->sz = sz;
+	}
+
+	return stats;
+}
+
+static int
+stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
+		   struct rte_graph *graph, struct graph_node *graph_node)
+{
+	struct rte_graph_cluster_stats *stats = *stats_in;
+	rte_node_t id = graph_node->node->id;
+	struct cluster_node *cluster;
+	struct rte_node *node;
+	rte_node_t count;
+
+	cluster = stats->clusters;
+
+	/* Iterate over cluster node array to find node ID match */
+	for (count = 0; count < stats->max_nodes; count++) {
+		/* Found an existing node in the reel */
+		if (cluster->stat.id == id) {
+			node = graph_node_id_to_ptr(graph, id);
+			if (node == NULL)
+				SET_ERR_JMP(
+					ENOENT, err,
+					"Failed to find node %s in graph %s",
+					graph_node->node->name, graph->name);
+
+			cluster->nodes[cluster->nb_nodes++] = node;
+			return 0;
+		}
+		cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
+	}
+
+	/* Hey, it is a new node, allocate space for it in the reel */
+	stats = realloc(stats, stats->sz + stats->cluster_node_size);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, err, "Realloc failed");
+
+	/* Clear the new struct cluster_node area */
+	cluster = RTE_PTR_ADD(stats, stats->sz),
+	memset(cluster, 0, stats->cluster_node_size);
+	memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
+	cluster->stat.id = graph_node->node->id;
+	cluster->stat.hz = rte_get_timer_hz();
+	node = graph_node_id_to_ptr(graph, id);
+	if (node == NULL)
+		SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s",
+			    graph_node->node->name, graph->name);
+	cluster->nodes[cluster->nb_nodes++] = node;
+
+	stats->sz += stats->cluster_node_size;
+	stats->max_nodes++;
+	*stats_in = stats;
+
+	return 0;
+err:
+	return -rte_errno;
+}
+
+static void
+stats_mem_fini(struct rte_graph_cluster_stats *stats)
+{
+	free(stats);
+}
+
+static void
+cluster_init(struct cluster *cluster)
+{
+	memset(cluster, 0, sizeof(*cluster));
+}
+
+static int
+cluster_add(struct cluster *cluster, struct graph *graph)
+{
+	rte_graph_t count;
+	size_t sz;
+
+	/* Skip the if graph is already added to cluster */
+	for (count = 0; count < cluster->nb_graphs; count++)
+		if (cluster->graphs[count] == graph)
+			return 0;
+
+	/* Expand the cluster if required to store graph objects */
+	if (cluster->nb_graphs + 1 > cluster->size) {
+		cluster->size = RTE_MAX(1, cluster->size * 2);
+		sz = sizeof(struct graph *) * cluster->size;
+		cluster->graphs = realloc(cluster->graphs, sz);
+		if (cluster->graphs == NULL)
+			SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
+	}
+
+	/* Add graph to cluster */
+	cluster->graphs[cluster->nb_graphs++] = graph;
+	return 0;
+
+free:
+	return -rte_errno;
+}
+
+static void
+cluster_fini(struct cluster *cluster)
+{
+	if (cluster->graphs)
+		free(cluster->graphs);
+}
+
+static int
+expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
+{
+	struct graph_head *graph_head = graph_list_head_get();
+	struct graph *graph;
+	bool found = false;
+
+	/* Check for pattern match */
+	STAILQ_FOREACH(graph, graph_head, next) {
+		if (fnmatch(pattern, graph->name, 0) == 0) {
+			if (cluster_add(cluster, graph))
+				goto fail;
+			found = true;
+		}
+	}
+	if (found == false)
+		SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
+			    pattern);
+
+	return 0;
+fail:
+	return -rte_errno;
+}
+
+struct rte_graph_cluster_stats *
+rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
+{
+	struct rte_graph_cluster_stats *stats, *rc = NULL;
+	struct graph_node *graph_node;
+	struct cluster cluster;
+	struct graph *graph;
+	const char *pattern;
+	rte_graph_t i;
+
+	/* Sanity checks */
+	if (!rte_graph_has_stats_feature())
+		SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
+
+	if (prm == NULL)
+		SET_ERR_JMP(EINVAL, fail, "Invalid param");
+
+	if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
+		SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
+
+	cluster_init(&cluster);
+
+	graph_spinlock_lock();
+	/* Expand graph pattern and add the graph to the cluster */
+	for (i = 0; i < prm->nb_graph_patterns; i++) {
+		pattern = prm->graph_patterns[i];
+		if (expand_pattern_to_cluster(&cluster, pattern))
+			goto bad_pattern;
+	}
+
+	/* Alloc the stats memory */
+	stats = stats_mem_init(&cluster, prm);
+	if (stats == NULL)
+		SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
+
+	/* Iterate over M(Graph) x N (Nodes in graph) */
+	for (i = 0; i < cluster.nb_graphs; i++) {
+		graph = cluster.graphs[i];
+		STAILQ_FOREACH(graph_node, &graph->node_list, next) {
+			struct rte_graph *graph_fp = graph->graph;
+			if (stats_mem_populate(&stats, graph_fp, graph_node))
+				goto realloc_fail;
+		}
+	}
+
+	/* Finally copy to hugepage memory to avoid pressure on rte_realloc */
+	rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
+	if (rc)
+		rte_memcpy(rc, stats, stats->sz);
+	else
+		SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
+
+realloc_fail:
+	stats_mem_fini(stats);
+bad_pattern:
+	graph_spinlock_unlock();
+	cluster_fini(&cluster);
+fail:
+	return rc;
+}
+
+void
+rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
+{
+	return rte_free(stat);
+}
+
+static inline void
+cluster_node_arregate_stats(struct cluster_node *cluster)
+{
+	uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+	struct rte_node *node;
+	rte_node_t count;
+
+	for (count = 0; count < cluster->nb_nodes; count++) {
+		node = cluster->nodes[count];
+
+		calls += node->total_calls;
+		objs += node->total_objs;
+		cycles += node->total_cycles;
+		realloc_count += node->realloc_count;
+	}
+
+	stat->calls = calls;
+	stat->objs = objs;
+	stat->cycles = cycles;
+	stat->ts = rte_get_timer_cycles();
+	stat->realloc_count = realloc_count;
+}
+
+static inline void
+cluster_node_store_prev_stats(struct cluster_node *cluster)
+{
+	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
+
+	stat->prev_ts = stat->ts;
+	stat->prev_calls = stat->calls;
+	stat->prev_objs = stat->objs;
+	stat->prev_cycles = stat->cycles;
+}
+
+void
+rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+	int rc = 0;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		cluster_node_arregate_stats(cluster);
+		if (!skip_cb)
+			rc = stat->fn(!count, (count == stat->max_nodes - 1),
+				      stat->cookie, &cluster->stat);
+		cluster_node_store_prev_stats(cluster);
+		if (rc)
+			break;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
+
+void
+rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
+{
+	struct cluster_node *cluster;
+	rte_node_t count;
+
+	cluster = stat->clusters;
+
+	for (count = 0; count < stat->max_nodes; count++) {
+		struct rte_graph_cluster_node_stats *node = &cluster->stat;
+
+		node->ts = 0;
+		node->calls = 0;
+		node->objs = 0;
+		node->cycles = 0;
+		node->prev_ts = 0;
+		node->prev_calls = 0;
+		node->prev_objs = 0;
+		node->prev_cycles = 0;
+		node->realloc_count = 0;
+		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
+	}
+}
diff --git a/lib/librte_graph/meson.build b/lib/librte_graph/meson.build
index fb203a5e2..929a17f84 100644
--- a/lib/librte_graph/meson.build
+++ b/lib/librte_graph/meson.build
@@ -4,7 +4,7 @@
 
 name = 'graph'
 
-sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_populate.c')
+sources = files('node.c', 'graph.c', 'graph_ops.c', 'graph_debug.c', 'graph_stats.c', 'graph_populate.c')
 headers = files('rte_graph.h', 'rte_graph_worker.h')
 allow_experimental_apis = true
 
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index 851f4772e..adf55d406 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -17,6 +17,11 @@ EXPERIMENTAL {
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
 
+	rte_graph_cluster_stats_create;
+	rte_graph_cluster_stats_destroy;
+	rte_graph_cluster_stats_get;
+	rte_graph_cluster_stats_reset;
+
 	rte_node_clone;
 	rte_node_dump;
 	rte_node_edge_count;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 12/29] graph: implement fastpath API routines
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (10 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 11/29] graph: implement stats support jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 13/29] graph: add unit test case jerinj
                           ` (18 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Jerin Jacob, Kiran Kumar K
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, pbhagavatula,
	ndabilpuram, xiao.w.wang, amo

From: Jerin Jacob <jerinj@marvell.com>

Adding implementation for rte_graph_walk() API. This will perform a walk
on the circular buffer and call the process function of each node
and collect the stats if stats collection is enabled.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md              |   1 +
 lib/librte_graph/graph.c               |  16 +
 lib/librte_graph/rte_graph_version.map |  10 +
 lib/librte_graph/rte_graph_worker.h    | 404 +++++++++++++++++++++++++
 4 files changed, 431 insertions(+)

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5cc50f750..fd2ff64d7 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -160,6 +160,7 @@ The public API headers are grouped by topics:
     [port_in_action]   (@ref rte_port_in_action.h)
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
+    [graph_worker]     (@ref rte_graph_worker.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_graph/graph.c b/lib/librte_graph/graph.c
index e811a7b38..0ea83df3d 100644
--- a/lib/librte_graph/graph.c
+++ b/lib/librte_graph/graph.c
@@ -473,6 +473,22 @@ __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node)
 	node->realloc_count++;
 }
 
+void __rte_noinline
+__rte_node_stream_alloc_size(struct rte_graph *graph, struct rte_node *node,
+			     uint16_t req_size)
+{
+	uint16_t size = node->size;
+
+	RTE_VERIFY(size != UINT16_MAX);
+	/* Allocate double amount of size to avoid immediate realloc */
+	size = RTE_MIN(UINT16_MAX, RTE_MAX(RTE_GRAPH_BURST_SIZE, req_size * 2));
+	node->objs = rte_realloc_socket(node->objs, size * sizeof(void *),
+					RTE_CACHE_LINE_SIZE, graph->socket);
+	RTE_VERIFY(node->objs);
+	node->size = size;
+	node->realloc_count++;
+}
+
 static int
 graph_to_dot(FILE *f, struct graph *graph)
 {
diff --git a/lib/librte_graph/rte_graph_version.map b/lib/librte_graph/rte_graph_version.map
index adf55d406..13b838752 100644
--- a/lib/librte_graph/rte_graph_version.map
+++ b/lib/librte_graph/rte_graph_version.map
@@ -3,6 +3,7 @@ EXPERIMENTAL {
 
 	__rte_node_register;
 	__rte_node_stream_alloc;
+	__rte_node_stream_alloc_size;
 
 	rte_graph_create;
 	rte_graph_destroy;
@@ -16,6 +17,7 @@ EXPERIMENTAL {
 	rte_graph_node_get;
 	rte_graph_node_get_by_name;
 	rte_graph_obj_dump;
+	rte_graph_walk;
 
 	rte_graph_cluster_stats_create;
 	rte_graph_cluster_stats_destroy;
@@ -28,10 +30,18 @@ EXPERIMENTAL {
 	rte_node_edge_get;
 	rte_node_edge_shrink;
 	rte_node_edge_update;
+	rte_node_enqueue;
+	rte_node_enqueue_x1;
+	rte_node_enqueue_x2;
+	rte_node_enqueue_x4;
+	rte_node_enqueue_next;
 	rte_node_from_name;
 	rte_node_id_to_name;
 	rte_node_list_dump;
 	rte_node_max_count;
+	rte_node_next_stream_get;
+	rte_node_next_stream_put;
+	rte_node_next_stream_move;
 
 	local: *;
 };
diff --git a/lib/librte_graph/rte_graph_worker.h b/lib/librte_graph/rte_graph_worker.h
index 42be8fd13..4c3ddcbde 100644
--- a/lib/librte_graph/rte_graph_worker.h
+++ b/lib/librte_graph/rte_graph_worker.h
@@ -99,6 +99,410 @@ struct rte_node {
 __rte_experimental
 void __rte_node_stream_alloc(struct rte_graph *graph, struct rte_node *node);
 
+/**
+ * @internal
+ *
+ * Allocate a stream with requested number of objects.
+ *
+ * If stream already exists then re-allocate it to a larger size.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param req_size
+ *   Number of objects to be allocated.
+ */
+__rte_experimental
+void __rte_node_stream_alloc_size(struct rte_graph *graph,
+				  struct rte_node *node, uint16_t req_size);
+
+/**
+ * Perform graph walk on the circular buffer and invoke the process function
+ * of the nodes and collect the stats.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup function.
+ *
+ * @see rte_graph_lookup()
+ */
+__rte_experimental
+static inline void
+rte_graph_walk(struct rte_graph *graph)
+{
+	const rte_graph_off_t *cir_start = graph->cir_start;
+	const rte_node_t mask = graph->cir_mask;
+	uint32_t head = graph->head;
+	struct rte_node *node;
+	uint64_t start;
+	uint16_t rc;
+	void **objs;
+
+	/*
+	 * Walk on the source node(s) ((cir_start - head) -> cir_start) and then
+	 * on the pending streams (cir_start -> (cir_start + mask) -> cir_start)
+	 * in a circular buffer fashion.
+	 *
+	 *	+-----+ <= cir_start - head [number of source nodes]
+	 *	|     |
+	 *	| ... | <= source nodes
+	 *	|     |
+	 *	+-----+ <= cir_start [head = 0] [tail = 0]
+	 *	|     |
+	 *	| ... | <= pending streams
+	 *	|     |
+	 *	+-----+ <= cir_start + mask
+	 */
+	while (likely(head != graph->tail)) {
+		node = RTE_PTR_ADD(graph, cir_start[(int32_t)head++]);
+		RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+		objs = node->objs;
+		rte_prefetch0(objs);
+
+		if (rte_graph_has_stats_feature()) {
+			start = rte_rdtsc();
+			rc = node->process(graph, node, objs, node->idx);
+			node->total_cycles += rte_rdtsc() - start;
+			node->total_calls++;
+			node->total_objs += rc;
+		} else {
+			node->process(graph, node, objs, node->idx);
+		}
+		node->idx = 0;
+		head = likely((int32_t)head > 0) ? head & mask : head;
+	}
+	graph->tail = 0;
+}
+
+/* Fast path helper functions */
+
+/**
+ * @internal
+ *
+ * Enqueue a given node to the tail of the graph reel.
+ *
+ * @param graph
+ *   Pointer Graph object.
+ * @param node
+ *   Pointer to node object to be enqueued.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_tail_update(struct rte_graph *graph, struct rte_node *node)
+{
+	uint32_t tail;
+
+	tail = graph->tail;
+	graph->cir_start[tail++] = node->off;
+	graph->tail = tail & graph->cir_mask;
+}
+
+/**
+ * @internal
+ *
+ * Enqueue sequence prologue function.
+ *
+ * Updates the node to tail of graph reel and resizes the number of objects
+ * available in the stream as needed.
+ *
+ * @param graph
+ *   Pointer to the graph object.
+ * @param node
+ *   Pointer to the node object.
+ * @param idx
+ *   Index at which the object enqueue starts from.
+ * @param space
+ *   Space required for the object enqueue.
+ */
+static __rte_always_inline void
+__rte_node_enqueue_prologue(struct rte_graph *graph, struct rte_node *node,
+			    const uint16_t idx, const uint16_t space)
+{
+
+	/* Add to the pending stream list if the node is new */
+	if (idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	if (unlikely(node->size < (idx + space)))
+		__rte_node_stream_alloc(graph, node);
+}
+
+/**
+ * @internal
+ *
+ * Get the node pointer from current node edge id.
+ *
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Edge id of the required node.
+ *
+ * @return
+ *   Pointer to the node denoted by the edge id.
+ */
+static __rte_always_inline struct rte_node *
+__rte_node_next_node_get(struct rte_node *node, rte_edge_t next)
+{
+	RTE_ASSERT(next < node->nb_edges);
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+	node = node->nodes[next];
+	RTE_ASSERT(node->fence == RTE_GRAPH_FENCE);
+
+	return node;
+}
+
+/**
+ * Enqueue the objs to next node for further processing and set
+ * the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param objs
+ *   Objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue(struct rte_graph *graph, struct rte_node *node,
+		 rte_edge_t next, void **objs, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, nb_objs);
+
+	rte_memcpy(&node->objs[idx], objs, nb_objs * sizeof(void *));
+	node->idx = idx + nb_objs;
+}
+
+/**
+ * Enqueue only one obj to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x1(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 1);
+
+	node->objs[idx++] = obj;
+	node->idx = idx;
+}
+
+/**
+ * Enqueue only two objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue two objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   Obj to enqueue.
+ * @param obj1
+ *   Obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x2(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 2);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->idx = idx;
+}
+
+/**
+ * Enqueue only four objs to next node for further processing and
+ * set the next node to pending state in the circular buffer.
+ * Same as rte_node_enqueue_x1 but enqueue four objs.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to enqueue objs.
+ * @param obj0
+ *   1st obj to enqueue.
+ * @param obj1
+ *   2nd obj to enqueue.
+ * @param obj2
+ *   3rd obj to enqueue.
+ * @param obj3
+ *   4th obj to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_x4(struct rte_graph *graph, struct rte_node *node,
+		    rte_edge_t next, void *obj0, void *obj1, void *obj2,
+		    void *obj3)
+{
+	node = __rte_node_next_node_get(node, next);
+	uint16_t idx = node->idx;
+
+	__rte_node_enqueue_prologue(graph, node, idx, 4);
+
+	node->objs[idx++] = obj0;
+	node->objs[idx++] = obj1;
+	node->objs[idx++] = obj2;
+	node->objs[idx++] = obj3;
+	node->idx = idx;
+}
+
+/**
+ * Enqueue objs to multiple next nodes for further processing and
+ * set the next nodes to pending state in the circular buffer.
+ * objs[i] will be enqueued to nexts[i].
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param nexts
+ *   List of relative next node indices to enqueue objs.
+ * @param objs
+ *   List of objs to enqueue.
+ * @param nb_objs
+ *   Number of objs to enqueue.
+ */
+__rte_experimental
+static inline void
+rte_node_enqueue_next(struct rte_graph *graph, struct rte_node *node,
+		      rte_edge_t *nexts, void **objs, uint16_t nb_objs)
+{
+	uint16_t i;
+
+	for (i = 0; i < nb_objs; i++)
+		rte_node_enqueue_x1(graph, node, nexts[i], objs[i]);
+}
+
+/**
+ * Get the stream of next node to enqueue the objs.
+ * Once done with the updating the objs, needs to call
+ * rte_node_next_stream_put to put the next node to pending state.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index to get stream.
+ * @param nb_objs
+ *   Requested free size of the next stream.
+ *
+ * @return
+ *   Valid next stream on success.
+ *
+ * @see rte_node_next_stream_put().
+ */
+__rte_experimental
+static inline void **
+rte_node_next_stream_get(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t nb_objs)
+{
+	node = __rte_node_next_node_get(node, next);
+	const uint16_t idx = node->idx;
+	uint16_t free_space = node->size - idx;
+
+	if (unlikely(free_space < nb_objs))
+		__rte_node_stream_alloc_size(graph, node, nb_objs);
+
+	return &node->objs[idx];
+}
+
+/**
+ * Put the next stream to pending state in the circular buffer
+ * for further processing. Should be invoked after rte_node_next_stream_get().
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param node
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index..
+ * @param idx
+ *   Number of objs updated in the stream after getting the stream using
+ *   rte_node_next_stream_get.
+ *
+ * @see rte_node_next_stream_get().
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_put(struct rte_graph *graph, struct rte_node *node,
+			 rte_edge_t next, uint16_t idx)
+{
+	if (unlikely(!idx))
+		return;
+
+	node = __rte_node_next_node_get(node, next);
+	if (node->idx == 0)
+		__rte_node_enqueue_tail_update(graph, node);
+
+	node->idx += idx;
+}
+
+/**
+ * Home run scenario, Enqueue all the objs of current node to next
+ * node in optimized way by swapping the streams of both nodes.
+ * Performs good when next node is already not in pending state.
+ * If next node is already in pending state then normal enqueue
+ * will be used.
+ *
+ * @param graph
+ *   Graph pointer returned from rte_graph_lookup().
+ * @param src
+ *   Current node pointer.
+ * @param next
+ *   Relative next node index.
+ */
+__rte_experimental
+static inline void
+rte_node_next_stream_move(struct rte_graph *graph, struct rte_node *src,
+			  rte_edge_t next)
+{
+	struct rte_node *dst = __rte_node_next_node_get(src, next);
+
+	/* Let swap the pointers if dst don't have valid objs */
+	if (likely(dst->idx == 0)) {
+		void **dobjs = dst->objs;
+		uint16_t dsz = dst->size;
+		dst->objs = src->objs;
+		dst->size = src->size;
+		src->objs = dobjs;
+		src->size = dsz;
+		dst->idx = src->idx;
+		__rte_node_enqueue_tail_update(graph, dst);
+	} else { /* Move the objects from src node to dst node */
+		rte_node_enqueue(graph, src, next, src->objs, src->idx);
+	}
+}
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 13/29] graph: add unit test case
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (11 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 12/29] graph: implement fastpath API routines jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 14/29] graph: add performance testcase jerinj
                           ` (17 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang, amo

From: Kiran Kumar K <kirankumark@marvell.com>

Adding the unit test to test the functionality of node and graph APIs.
Testing includes registering a node, cloning a node, creating a graph,
perform graph walk, collecting stats and all node and graph debug APIs.

example command to test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 app/test/Makefile     |   6 +
 app/test/meson.build  |   6 +-
 app/test/test_graph.c | 819 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 830 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_graph.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 1f080d162..ce2e08e12 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -221,6 +221,10 @@ SRCS-y += test_event_timer_adapter.c
 SRCS-y += test_event_crypto_adapter.c
 endif
 
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
+SRCS-y += test_graph.c
+endif
+
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
 SRCS-y += test_rawdev.c
 endif
@@ -240,6 +244,8 @@ endif
 CFLAGS += -DALLOW_EXPERIMENTAL_API
 
 CFLAGS += -O3
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
 CFLAGS += $(WERROR_FLAGS)
 
 LDLIBS += -lm
diff --git a/app/test/meson.build b/app/test/meson.build
index 351d29cb6..3cf850584 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -51,6 +51,7 @@ test_sources = files('commands.c',
 	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
+	'test_graph.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
@@ -151,7 +152,8 @@ test_deps = ['acl',
 	'rib',
 	'ring',
 	'stack',
-	'timer'
+	'timer',
+	'graph'
 ]
 
 # Each test is marked with flag true/false
@@ -362,6 +364,8 @@ endif
 
 # specify -D_GNU_SOURCE unconditionally
 cflags += '-D_GNU_SOURCE'
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
 
 test_dep_objs = []
 if dpdk_conf.has('RTE_LIBRTE_COMPRESSDEV')
diff --git a/app/test/test_graph.c b/app/test/test_graph.c
new file mode 100644
index 000000000..f5c957f82
--- /dev/null
+++ b/app/test/test_graph.c
@@ -0,0 +1,819 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static uint16_t test_node_worker_source(struct rte_graph *graph,
+					struct rte_node *node, void **objs,
+					uint16_t nb_objs);
+
+static uint16_t test_node0_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node1_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node2_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node3_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+#define MBUFF_SIZE 512
+#define MAX_NODES  4
+
+static struct rte_mbuf mbuf[MAX_NODES + 1][MBUFF_SIZE];
+static void *mbuf_p[MAX_NODES + 1][MBUFF_SIZE];
+static rte_graph_t graph_id;
+static uint64_t obj_stats[MAX_NODES + 1];
+static uint64_t fn_calls[MAX_NODES + 1];
+
+const char *node_patterns[] = {
+	"test_node_source1",	   "test_node00",
+	"test_node00-test_node11", "test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+const char *node_names[] = {
+	"test_node00",
+	"test_node00-test_node11",
+	"test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+struct test_node_register {
+	char name[RTE_NODE_NAMESIZE];
+	rte_node_process_t process;
+	uint16_t nb_edges;
+	const char *next_nodes[MAX_NODES];
+};
+
+typedef struct {
+	uint32_t idx;
+	struct test_node_register node;
+} test_node_t;
+
+typedef struct {
+	test_node_t test_node[MAX_NODES];
+} test_main_t;
+
+static test_main_t test_main = {
+	.test_node = {
+		{
+			.node = {
+					.name = "test_node00",
+					.process = test_node0_worker,
+					.nb_edges = 2,
+					.next_nodes = {"test_node00-"
+						       "test_node11",
+						       "test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node11",
+					.process = test_node1_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node22",
+					.process = test_node2_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node33"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node33",
+					.process = test_node3_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00"},
+				},
+		},
+	},
+};
+
+static int
+node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	*(uint32_t *)node->ctx = node->id;
+
+	return 0;
+}
+
+static struct rte_node_register test_node_source = {
+	.name = "test_node_source1",
+	.process = test_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.nb_edges = 2,
+	.init = node_init,
+	.next_nodes = {"test_node00", "test_node00-test_node11"},
+};
+RTE_NODE_REGISTER(test_node_source);
+
+static struct rte_node_register test_node0 = {
+	.name = "test_node00",
+	.process = test_node0_worker,
+	.init = node_init,
+};
+RTE_NODE_REGISTER(test_node0);
+
+uint16_t
+test_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	uint32_t obj_node0 = rand() % 100, obj_node1;
+	test_main_t *tm = &test_main;
+	struct rte_mbuf *data;
+	void **next_stream;
+	rte_node_t next;
+	uint32_t i;
+
+	RTE_SET_USED(objs);
+	nb_objs = RTE_GRAPH_BURST_SIZE;
+
+	/* Prepare stream for next node 0 */
+	obj_node0 = nb_objs * obj_node0 * 0.01;
+	next = 0;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node0);
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[0][i];
+		data->udata64 = ((uint64_t)tm->test_node[0].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][i];
+	}
+	rte_node_next_stream_put(graph, node, next, obj_node0);
+
+	/* Prepare stream for next node 1 */
+	obj_node1 = nb_objs - obj_node0;
+	next = 1;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node1);
+	for (i = 0; i < obj_node1; i++) {
+		data = &mbuf[0][obj_node0 + i];
+		data->udata64 = ((uint64_t)tm->test_node[1].idx << 32) | i;
+		if ((i + 1) == obj_node1)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][obj_node0 + i];
+	}
+
+	rte_node_next_stream_put(graph, node, next, obj_node1);
+	obj_stats[0] += nb_objs;
+	fn_calls[0] += 1;
+	return nb_objs;
+}
+
+uint16_t
+test_node0_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+
+	if (*(uint32_t *)node->ctx == test_node0.id) {
+		uint32_t obj_node0 = rand() % 100, obj_node1;
+		struct rte_mbuf *data;
+		uint8_t second_pass = 0;
+		uint32_t count = 0;
+		uint32_t i;
+
+		obj_stats[1] += nb_objs;
+		fn_calls[1] += 1;
+
+		for (i = 0; i < nb_objs; i++) {
+			data = (struct rte_mbuf *)objs[i];
+			if ((data->udata64 >> 32) != tm->test_node[0].idx) {
+				printf("Data idx miss match at node 0, expected"
+				       " = %u got = %u\n",
+				       tm->test_node[0].idx,
+				       (uint32_t)(data->udata64 >> 32));
+				goto end;
+			}
+
+			if ((data->udata64 & 0xffff) != (i - count)) {
+				printf("Expected buff count miss match at "
+				       "node 0\n");
+				goto end;
+			}
+
+			if (data->udata64 & (0x1 << 16))
+				count = i + 1;
+			if (data->udata64 & (0x1 << 17))
+				second_pass = 1;
+		}
+
+		if (count != i) {
+			printf("Count mismatch at node 0\n");
+			goto end;
+		}
+
+		obj_node0 = nb_objs * obj_node0 * 0.01;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[1][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[1].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[1][0],
+				 obj_node0);
+
+		obj_node1 = nb_objs - obj_node0;
+		for (i = 0; i < obj_node1; i++) {
+			data = &mbuf[1][obj_node0 + i];
+			data->udata64 =
+				((uint64_t)tm->test_node[2].idx << 32) | i;
+			if ((i + 1) == obj_node1)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 1, (void **)&mbuf_p[1][obj_node0],
+				 obj_node1);
+
+	} else if (*(uint32_t *)node->ctx == tm->test_node[1].idx) {
+		test_node1_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[2].idx) {
+		test_node2_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[3].idx) {
+		test_node3_worker(graph, node, objs, nb_objs);
+	} else {
+		printf("Unexpected node context\n");
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node1_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	uint32_t obj_node0 = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t i;
+
+	obj_stats[2] += nb_objs;
+	fn_calls[2] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[1].idx) {
+			printf("Data idx miss match at node 1, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[1].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 1\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 1\n");
+		goto end;
+	}
+
+	obj_node0 = nb_objs;
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[2][i];
+		data->udata64 = ((uint64_t)tm->test_node[2].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		if (second_pass)
+			data->udata64 |= (1 << 17);
+	}
+	rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[2][0], obj_node0);
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node2_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[3] += nb_objs;
+	fn_calls[3] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[2].idx) {
+			printf("Data idx miss match at node 2, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[2].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 2\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 2\n");
+		goto end;
+	}
+
+	if (!second_pass) {
+		obj_node0 = nb_objs;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[3][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[3].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[3][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node3_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[4] += nb_objs;
+	fn_calls[4] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[3].idx) {
+			printf("Data idx miss match at node 3, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[3].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 3\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 3\n");
+		goto end;
+	}
+
+	if (second_pass) {
+		printf("Unexpected buffers are at node 3\n");
+		goto end;
+	} else {
+		obj_node0 = nb_objs * 2;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[4][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[0].idx << 32) | i;
+			data->udata64 |= (1 << 17);
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[4][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+static int
+test_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	int i;
+
+	/* Verify the name with ID */
+	for (i = 1; i < MAX_NODES; i++) {
+		char *name = rte_node_id_to_name(tm->test_node[i].idx);
+		if (strcmp(name, node_names[i]) != 0) {
+			printf("Test node name verify by ID = %d failed "
+			       "Expected = %s, got %s\n",
+			       i, node_names[i], name);
+			return -1;
+		}
+	}
+
+	/* Verify by name */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t idx = rte_node_from_name(node_names[i]);
+		if (idx != tm->test_node[i].idx) {
+			printf("Test node ID verify by name = %s failed "
+			       "Expected = %d, got %d\n",
+			       node_names[i], tm->test_node[i].idx, idx);
+			return -1;
+		}
+	}
+
+	/* Verify edge count */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t count = rte_node_edge_count(tm->test_node[i].idx);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+	}
+
+	/* Verify edge names */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t j, count;
+		char **next_edges;
+
+		count = rte_node_edge_get(tm->test_node[i].idx, NULL);
+		if (count != tm->test_node[i].node.nb_edges * sizeof(char *)) {
+			printf("Test number of edge count for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+		next_edges = malloc(count);
+		count = rte_node_edge_get(tm->test_node[i].idx, next_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		for (j = 0; j < count; j++) {
+			if (strcmp(next_edges[j],
+				   tm->test_node[i].node.next_nodes[j]) != 0) {
+				printf("Edge name miss match, expected = %s got = %s\n",
+				       tm->test_node[i].node.next_nodes[j],
+				       next_edges[j]);
+				return -1;
+			}
+		}
+		free(next_edges);
+	}
+
+	return 0;
+}
+
+static int
+test_node_clone(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id, dummy_id;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	tm->test_node[0].idx = node_id;
+
+	/* Clone with same name, should fail */
+	dummy_id = rte_node_clone(node_id, "test_node00");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid id when clone with same name, Expecting fail\n");
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		tm->test_node[i].idx =
+			rte_node_clone(node_id, tm->test_node[i].node.name);
+		if (rte_node_is_invalid(tm->test_node[i].idx)) {
+			printf("Got invalid node id\n");
+			return -1;
+		}
+	}
+
+	/* Clone from cloned node should fail */
+	dummy_id = rte_node_clone(tm->test_node[1].idx, "dummy_node");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid node id when cloning from cloned node, expected fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_update_edges(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id;
+	uint16_t count;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	count = rte_node_edge_update(node_id, 0,
+				     tm->test_node[0].node.next_nodes,
+				     tm->test_node[0].node.nb_edges);
+	if (count != tm->test_node[0].node.nb_edges) {
+		printf("Update edges failed expected: %d got = %d\n",
+		       tm->test_node[0].node.nb_edges, count);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		count = rte_node_edge_update(tm->test_node[i].idx, 0,
+					     tm->test_node[i].node.next_nodes,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Update edges failed expected: %d got = %d\n",
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		count = rte_node_edge_shrink(tm->test_node[i].idx,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Shrink edges failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_create_graph(void)
+{
+	static const char *node_patterns_dummy[] = {
+		"test_node_source1",	   "test_node00",
+		"test_node00-test_node11", "test_node00-test_node22",
+		"test_node00-test_node33", "test_node00-dummy_node",
+	};
+	struct rte_graph_param gconf = {
+		.socket_id = SOCKET_ID_ANY,
+		.nb_node_patterns = 6,
+		.node_patterns = node_patterns_dummy,
+	};
+	uint32_t dummy_node_id;
+	uint32_t node_id;
+
+	node_id = rte_node_from_name("test_node00");
+	dummy_node_id = rte_node_clone(node_id, "dummy_node");
+	if (rte_node_is_invalid(dummy_node_id)) {
+		printf("Got invalid node id\n");
+		return -1;
+	}
+
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id != RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation success with isolated node, expected graph creation fail\n");
+		return -1;
+	}
+
+	gconf.nb_node_patterns = 5;
+	gconf.node_patterns = node_patterns;
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+test_graph_walk(void)
+{
+	struct rte_graph *graph = rte_graph_lookup("worker0");
+	int i;
+
+	if (!graph) {
+		printf("Graph lookup failed\n");
+		return -1;
+	}
+
+	for (i = 0; i < 5; i++)
+		rte_graph_walk(graph);
+	return 0;
+}
+
+static int
+test_graph_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	struct rte_node *node;
+	int i;
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get(graph_id, tm->test_node[i].idx);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get_by_name("worker0", node_names[i]);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+graph_cluster_stats_cb_t(bool is_first, bool is_last, void *cookie,
+			 const struct rte_graph_cluster_node_stats *st)
+{
+	int i;
+
+	RTE_SET_USED(is_first);
+	RTE_SET_USED(is_last);
+	RTE_SET_USED(cookie);
+
+	for (i = 0; i < MAX_NODES + 1; i++) {
+		rte_node_t id = rte_node_from_name(node_patterns[i]);
+		if (id == st->id) {
+			if (obj_stats[i] != st->objs) {
+				printf("Obj count miss match for node = %s expected = %"PRId64", got=%"PRId64"\n",
+				       node_patterns[i], obj_stats[i],
+				       st->objs);
+				return -1;
+			}
+
+			if (fn_calls[i] != st->calls) {
+				printf("Func call miss match for node = %s expected = %"PRId64", got = %"PRId64"\n",
+				       node_patterns[i], fn_calls[i],
+				       st->calls);
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+test_print_stats(void)
+{
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker0";
+
+	if (!rte_graph_has_stats_feature())
+		return 0;
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+	s_param.fn = graph_cluster_stats_cb_t;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL) {
+		printf("Unable to get stats\n");
+		return -1;
+	}
+	/* Clear screen and move to top left */
+	rte_graph_cluster_stats_get(stats, 0);
+	rte_graph_cluster_stats_destroy(stats);
+
+	return 0;
+}
+
+static int
+graph_setup(void)
+{
+	int i, j;
+
+	for (i = 0; i <= MAX_NODES; i++) {
+		for (j = 0; j < MBUFF_SIZE; j++)
+			mbuf_p[i][j] = &mbuf[i][j];
+	}
+	if (test_node_clone()) {
+		printf("test_node_clone: fail\n");
+		return -1;
+	}
+	printf("test_node_clone: pass\n");
+
+	return 0;
+}
+
+static void
+graph_teardown(void)
+{
+	int id;
+
+	id = rte_graph_destroy(rte_graph_from_name("worker0"));
+	if (id)
+		printf("Graph Destroy failed\n");
+}
+
+static struct unit_test_suite graph_testsuite = {
+	.suite_name = "Graph library test suite",
+	.setup = graph_setup,
+	.teardown = graph_teardown,
+	.unit_test_cases = {
+		TEST_CASE(test_update_edges),
+		TEST_CASE(test_lookup_functions),
+		TEST_CASE(test_create_graph),
+		TEST_CASE(test_graph_lookup_functions),
+		TEST_CASE(test_graph_walk),
+		TEST_CASE(test_print_stats),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+graph_autotest_fn(void)
+{
+	return unit_test_suite_runner(&graph_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_autotest, graph_autotest_fn);
+
+static int
+test_node_list_dump(void)
+{
+	rte_node_list_dump(stdout);
+
+	return TEST_SUCCESS;
+}
+REGISTER_TEST_COMMAND(node_list_dump, test_node_list_dump);
+
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 14/29] graph: add performance testcase
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (12 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 13/29] graph: add unit test case jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 15/29] node: add log infra and null node jerinj
                           ` (16 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang, amo

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add unit test framework to create and test performance of various graph
models.

example command to test:

echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 app/test/Makefile          |    1 +
 app/test/meson.build       |    1 +
 app/test/test_graph_perf.c | 1057 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1059 insertions(+)
 create mode 100644 app/test/test_graph_perf.c

diff --git a/app/test/Makefile b/app/test/Makefile
index ce2e08e12..77276f300 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -223,6 +223,7 @@ endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
 SRCS-y += test_graph.c
+SRCS-y += test_graph_perf.c
 endif
 
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
diff --git a/app/test/meson.build b/app/test/meson.build
index 3cf850584..9006cc074 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -52,6 +52,7 @@ test_sources = files('commands.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
 	'test_graph.c',
+	'test_graph_perf.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
diff --git a/app/test/test_graph_perf.c b/app/test/test_graph_perf.c
new file mode 100644
index 000000000..3089fb24c
--- /dev/null
+++ b/app/test/test_graph_perf.c
@@ -0,0 +1,1057 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_lcore.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+#define TEST_GRAPH_PERF_MZ	     "graph_perf_data"
+#define TEST_GRAPH_SRC_NAME	     "test_graph_perf_source"
+#define TEST_GRAPH_SRC_BRST_ONE_NAME "test_graph_perf_source_one"
+#define TEST_GRAPH_WRK_NAME	     "test_graph_perf_worker"
+#define TEST_GRAPH_SNK_NAME	     "test_graph_perf_sink"
+
+#define SOURCES(map)	     RTE_DIM(map)
+#define STAGES(map)	     RTE_DIM(map)
+#define NODES_PER_STAGE(map) RTE_DIM(map[0])
+#define SINKS(map)	     RTE_DIM(map[0])
+
+#define MAX_EDGES_PER_NODE 7
+
+struct test_node_data {
+	uint8_t node_id;
+	uint8_t is_sink;
+	uint8_t next_nodes[MAX_EDGES_PER_NODE];
+	uint8_t next_percentage[MAX_EDGES_PER_NODE];
+};
+
+struct test_graph_perf {
+	uint16_t nb_nodes;
+	rte_graph_t graph_id;
+	struct test_node_data *node_data;
+};
+
+struct graph_lcore_data {
+	uint8_t done;
+	rte_graph_t graph_id;
+};
+
+static struct test_node_data *
+graph_get_node_data(struct test_graph_perf *graph_data, rte_node_t id)
+{
+	struct test_node_data *node_data = NULL;
+	int i;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		if (graph_data->node_data[i].node_id == id) {
+			node_data = &graph_data->node_data[i];
+			break;
+		}
+
+	return node_data;
+}
+
+static int
+test_node_ctx_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	struct test_graph_perf *graph_data;
+	struct test_node_data *node_data;
+	const struct rte_memzone *mz;
+	rte_node_t nid = node->id;
+	rte_edge_t edge = 0;
+	int i;
+
+	RTE_SET_USED(graph);
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+	node_data = graph_get_node_data(graph_data, nid);
+	node->ctx[0] = node->nb_edges;
+	for (i = 0; i < node->nb_edges && !node_data->is_sink; i++, edge++) {
+		node->ctx[i + 1] = edge;
+		node->ctx[i + 9] = node_data->next_percentage[i];
+	}
+
+	return 0;
+}
+
+/* Source node function */
+static uint16_t
+test_perf_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			     void **objs, uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9] * RTE_GRAPH_BURST_SIZE) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return RTE_GRAPH_BURST_SIZE;
+}
+
+static struct rte_node_register test_graph_perf_source = {
+	.name = TEST_GRAPH_SRC_NAME,
+	.process = test_perf_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source);
+
+static uint16_t
+test_perf_node_worker_source_burst_one(struct rte_graph *graph,
+				       struct rte_node *node, void **objs,
+				       uint16_t nb_objs)
+{
+	uint16_t count;
+	int i;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	/* Create a proportional stream for every next */
+	for (i = 0; i < node->ctx[0]; i++) {
+		count = (node->ctx[i + 9]) / 100;
+		rte_node_next_stream_get(graph, node, node->ctx[i + 1], count);
+		rte_node_next_stream_put(graph, node, node->ctx[i + 1], count);
+	}
+
+	return 1;
+}
+
+static struct rte_node_register test_graph_perf_source_burst_one = {
+	.name = TEST_GRAPH_SRC_BRST_ONE_NAME,
+	.process = test_perf_node_worker_source_burst_one,
+	.flags = RTE_NODE_SOURCE_F,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_source_burst_one);
+
+/* Worker node function */
+static uint16_t
+test_perf_node_worker(struct rte_graph *graph, struct rte_node *node,
+		      void **objs, uint16_t nb_objs)
+{
+	uint16_t next = 0;
+	uint16_t enq = 0;
+	uint16_t count;
+	int i;
+
+	/* Move stream for single next node */
+	if (node->ctx[0] == 1) {
+		rte_node_next_stream_move(graph, node, node->ctx[1]);
+		return nb_objs;
+	}
+
+	/* Enqueue objects to next nodes proportionally */
+	for (i = 0; i < node->ctx[0]; i++) {
+		next = node->ctx[i + 1];
+		count = (node->ctx[i + 9] * nb_objs) / 100;
+		enq += count;
+		while (count) {
+			switch (count & (4 - 1)) {
+			case 0:
+				rte_node_enqueue_x4(graph, node, next, objs[0],
+						    objs[1], objs[2], objs[3]);
+				objs += 4;
+				count -= 4;
+				break;
+			case 1:
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 1;
+				count -= 1;
+				break;
+			case 2:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				objs += 2;
+				count -= 2;
+				break;
+			case 3:
+				rte_node_enqueue_x2(graph, node, next, objs[0],
+						    objs[1]);
+				rte_node_enqueue_x1(graph, node, next, objs[0]);
+				objs += 3;
+				count -= 3;
+				break;
+			}
+		}
+	}
+
+	if (enq != nb_objs)
+		rte_node_enqueue(graph, node, next, objs, nb_objs - enq);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_worker = {
+	.name = TEST_GRAPH_WRK_NAME,
+	.process = test_perf_node_worker,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_worker);
+
+/* Last node in graph a.k.a sink node */
+static uint16_t
+test_perf_node_sink(struct rte_graph *graph, struct rte_node *node, void **objs,
+		    uint16_t nb_objs)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register test_graph_perf_sink = {
+	.name = TEST_GRAPH_SNK_NAME,
+	.process = test_perf_node_sink,
+	.init = test_node_ctx_init,
+};
+
+RTE_NODE_REGISTER(test_graph_perf_sink);
+
+static int
+graph_perf_setup(void)
+{
+	if (rte_lcore_count() < 2) {
+		printf("Test requires at least 2 lcores\n");
+		return TEST_SKIPPED;
+	}
+
+	return 0;
+}
+
+static void
+graph_perf_teardown(void)
+{
+}
+
+static inline rte_node_t
+graph_node_get(const char *pname, char *nname)
+{
+	rte_node_t pnode_id = rte_node_from_name(pname);
+	char lookup_name[RTE_NODE_NAMESIZE];
+	rte_node_t node_id;
+
+	snprintf(lookup_name, RTE_NODE_NAMESIZE, "%s-%s", pname, nname);
+	node_id = rte_node_from_name(lookup_name);
+
+	if (node_id != RTE_NODE_ID_INVALID) {
+		if (rte_node_edge_count(node_id))
+			rte_node_edge_shrink(node_id, 0);
+		return node_id;
+	}
+
+	return rte_node_clone(pnode_id, nname);
+}
+
+static uint16_t
+graph_node_count_edges(uint32_t stage, uint16_t node, uint16_t nodes_per_stage,
+		       uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+		       char *ename[], struct test_node_data *node_data,
+		       rte_node_t **node_map)
+{
+	uint8_t total_percent = 0;
+	uint16_t edges = 0;
+	int i;
+
+	for (i = 0; i < nodes_per_stage && edges < MAX_EDGES_PER_NODE; i++) {
+		if (edge_map[stage + 1][i][node]) {
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[stage + 1][i]));
+			node_data->next_nodes[edges] = node_map[stage + 1][i];
+			node_data->next_percentage[edges] =
+				edge_map[stage + 1][i][node];
+			edges++;
+			total_percent += edge_map[stage + 1][i][node];
+		}
+	}
+
+	if (edges >= MAX_EDGES_PER_NODE || (edges && total_percent != 100)) {
+		for (i = 0; i < edges; i++)
+			free(ename[i]);
+		return RTE_EDGE_ID_INVALID;
+	}
+
+	return edges;
+}
+
+static int
+graph_init(const char *gname, uint8_t nb_srcs, uint8_t nb_sinks,
+	   uint32_t stages, uint16_t nodes_per_stage,
+	   uint8_t src_map[][nodes_per_stage], uint8_t snk_map[][nb_sinks],
+	   uint8_t edge_map[][nodes_per_stage][nodes_per_stage],
+	   uint8_t burst_one)
+{
+	struct test_graph_perf *graph_data;
+	char nname[RTE_NODE_NAMESIZE / 2];
+	struct test_node_data *node_data;
+	char *ename[nodes_per_stage];
+	struct rte_graph_param gconf;
+	const struct rte_memzone *mz;
+	uint8_t total_percent = 0;
+	rte_node_t *src_nodes;
+	rte_node_t *snk_nodes;
+	rte_node_t **node_map;
+	char **node_patterns;
+	rte_graph_t graph_id;
+	rte_edge_t edges;
+	rte_edge_t count;
+	uint32_t i, j, k;
+
+	mz = rte_memzone_reserve(TEST_GRAPH_PERF_MZ,
+				 sizeof(struct test_graph_perf), 0, 0);
+	if (mz == NULL) {
+		printf("Failed to allocate graph common memory\n");
+		return -ENOMEM;
+	}
+
+	graph_data = mz->addr;
+	graph_data->nb_nodes = 0;
+	graph_data->node_data =
+		malloc(sizeof(struct test_node_data) *
+		       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (graph_data->node_data == NULL) {
+		printf("Failed to reserve memzone for graph data\n");
+		goto memzone_free;
+	}
+
+	node_patterns = malloc(sizeof(char *) *
+			       (nb_srcs + nb_sinks + stages * nodes_per_stage));
+	if (node_patterns == NULL) {
+		printf("Failed to reserve memory for node patterns\n");
+		goto data_free;
+	}
+
+	src_nodes = malloc(sizeof(rte_node_t) * nb_srcs);
+	if (src_nodes == NULL) {
+		printf("Failed to reserve memory for src nodes\n");
+		goto pattern_free;
+	}
+
+	snk_nodes = malloc(sizeof(rte_node_t) * nb_sinks);
+	if (snk_nodes == NULL) {
+		printf("Failed to reserve memory for snk nodes\n");
+		goto src_free;
+	}
+
+	node_map = malloc(sizeof(rte_node_t *) * stages +
+			  sizeof(rte_node_t) * nodes_per_stage * stages);
+	if (node_map == NULL) {
+		printf("Failed to reserve memory for node map\n");
+		goto snk_free;
+	}
+
+	/* Setup the Graph */
+	for (i = 0; i < stages; i++) {
+		node_map[i] =
+			(rte_node_t *)(node_map + stages) + nodes_per_stage * i;
+		for (j = 0; j < nodes_per_stage; j++) {
+			total_percent = 0;
+			for (k = 0; k < nodes_per_stage; k++)
+				total_percent += edge_map[i][j][k];
+			if (!total_percent)
+				continue;
+			node_patterns[graph_data->nb_nodes] =
+				malloc(RTE_NODE_NAMESIZE);
+			if (node_patterns[graph_data->nb_nodes] == NULL) {
+				printf("Failed to create memory for pattern\n");
+				goto pattern_name_free;
+			}
+
+			/* Clone a worker node */
+			snprintf(nname, sizeof(nname), "%d-%d", i, j);
+			node_map[i][j] =
+				graph_node_get(TEST_GRAPH_WRK_NAME, nname);
+			if (node_map[i][j] == RTE_NODE_ID_INVALID) {
+				printf("Failed to create node[%s]\n", nname);
+				graph_data->nb_nodes++;
+				goto pattern_name_free;
+			}
+			snprintf(node_patterns[graph_data->nb_nodes],
+				 RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[i][j]));
+			node_data =
+				&graph_data->node_data[graph_data->nb_nodes];
+			node_data->node_id = node_map[i][j];
+			node_data->is_sink = false;
+			graph_data->nb_nodes++;
+		}
+	}
+
+	for (i = 0; i < stages - 1; i++) {
+		for (j = 0; j < nodes_per_stage; j++) {
+			/* Count edges i.e connections of worker node to next */
+			node_data =
+				graph_get_node_data(graph_data, node_map[i][j]);
+			edges = graph_node_count_edges(i, j, nodes_per_stage,
+						       edge_map, ename,
+						       node_data, node_map);
+			if (edges == RTE_EDGE_ID_INVALID) {
+				printf("Invalid edge configuration\n");
+				goto pattern_name_free;
+			}
+			if (!edges)
+				continue;
+
+			/* Connect a node in stage 'i' to nodes
+			 * in stage 'i + 1' with edges.
+			 */
+			count = rte_node_edge_update(
+				node_map[i][j], 0,
+				(const char **)(uintptr_t)ename, edges);
+			for (k = 0; k < edges; k++)
+				free(ename[k]);
+			if (count != edges) {
+				printf("Couldn't add edges %d %d\n", edges,
+				       count);
+				goto pattern_name_free;
+			}
+		}
+	}
+
+	/* Setup Source nodes */
+	for (i = 0; i < nb_srcs; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+		/* Clone a source node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		src_nodes[i] =
+			graph_node_get(burst_one ? TEST_GRAPH_SRC_BRST_ONE_NAME
+						 : TEST_GRAPH_SRC_NAME,
+				       nname);
+		if (src_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(src_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = src_nodes[i];
+		node_data->is_sink = false;
+		graph_data->nb_nodes++;
+
+		/* Prepare next node list  to connect to */
+		for (j = 0; j < nodes_per_stage; j++) {
+			if (!src_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(node_map[0][j]));
+			node_data->next_nodes[edges] = node_map[0][j];
+			node_data->next_percentage[edges] = src_map[i][j];
+			edges++;
+			total_percent += src_map[i][j];
+		}
+
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[j]);
+			goto pattern_name_free;
+		}
+
+		/* Connect to list of next nodes using edges */
+		count = rte_node_edge_update(src_nodes[i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Setup Sink nodes */
+	for (i = 0; i < nb_sinks; i++) {
+		node_patterns[graph_data->nb_nodes] = malloc(RTE_NODE_NAMESIZE);
+		if (node_patterns[graph_data->nb_nodes] == NULL) {
+			printf("Failed to create memory for pattern\n");
+			goto pattern_name_free;
+		}
+
+		/* Clone a sink node */
+		snprintf(nname, sizeof(nname), "%d", i);
+		snk_nodes[i] = graph_node_get(TEST_GRAPH_SNK_NAME, nname);
+		if (snk_nodes[i] == RTE_NODE_ID_INVALID) {
+			printf("Failed to create node[%s]\n", nname);
+			graph_data->nb_nodes++;
+			goto pattern_name_free;
+		}
+		snprintf(node_patterns[graph_data->nb_nodes], RTE_NODE_NAMESIZE,
+			 "%s", rte_node_id_to_name(snk_nodes[i]));
+		node_data = &graph_data->node_data[graph_data->nb_nodes];
+		node_data->node_id = snk_nodes[i];
+		node_data->is_sink = true;
+		graph_data->nb_nodes++;
+	}
+
+	/* Connect last stage worker nodes to sink nodes */
+	for (i = 0; i < nodes_per_stage; i++) {
+		edges = 0;
+		total_percent = 0;
+		node_data = graph_get_node_data(graph_data,
+						node_map[stages - 1][i]);
+		/* Prepare list of sink nodes to connect to */
+		for (j = 0; j < nb_sinks; j++) {
+			if (!snk_map[i][j])
+				continue;
+			ename[edges] = malloc(sizeof(char) * RTE_NODE_NAMESIZE);
+			snprintf(ename[edges], RTE_NODE_NAMESIZE, "%s",
+				 rte_node_id_to_name(snk_nodes[j]));
+			node_data->next_nodes[edges] = snk_nodes[j];
+			node_data->next_percentage[edges] = snk_map[i][j];
+			edges++;
+			total_percent += snk_map[i][j];
+		}
+		if (!edges)
+			continue;
+		if (edges >= MAX_EDGES_PER_NODE || total_percent != 100) {
+			printf("Invalid edge configuration\n");
+			for (j = 0; j < edges; j++)
+				free(ename[i]);
+			goto pattern_name_free;
+		}
+
+		/* Connect a worker node to a list of sink nodes */
+		count = rte_node_edge_update(node_map[stages - 1][i], 0,
+					     (const char **)(uintptr_t)ename,
+					     edges);
+		for (k = 0; k < edges; k++)
+			free(ename[k]);
+		if (count != edges) {
+			printf("Couldn't add edges %d %d\n", edges, count);
+			goto pattern_name_free;
+		}
+	}
+
+	/* Create a Graph */
+	gconf.socket_id = SOCKET_ID_ANY;
+	gconf.nb_node_patterns = graph_data->nb_nodes;
+	gconf.node_patterns = (const char **)(uintptr_t)node_patterns;
+
+	graph_id = rte_graph_create(gname, &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		goto pattern_name_free;
+	}
+	graph_data->graph_id = graph_id;
+
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+	free(snk_nodes);
+	free(src_nodes);
+	free(node_patterns);
+	return 0;
+
+pattern_name_free:
+	for (i = 0; i < graph_data->nb_nodes; i++)
+		free(node_patterns[i]);
+snk_free:
+	free(snk_nodes);
+src_free:
+	free(src_nodes);
+pattern_free:
+	free(node_patterns);
+data_free:
+	free(graph_data->node_data);
+memzone_free:
+	rte_memzone_free(mz);
+	return -ENOMEM;
+}
+
+/* Worker thread function */
+static int
+_graph_perf_wrapper(void *args)
+{
+	struct graph_lcore_data *data = args;
+	struct rte_graph *graph;
+
+	/* Lookup graph */
+	graph = rte_graph_lookup(rte_graph_id_to_name(data->graph_id));
+
+	/* Graph walk until done */
+	while (!data->done)
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
+static int
+measure_perf_get(rte_graph_t graph_id)
+{
+	const char *pattern = rte_graph_id_to_name(graph_id);
+	uint32_t lcore_id = rte_get_next_lcore(-1, 1, 0);
+	struct rte_graph_cluster_stats_param param;
+	struct rte_graph_cluster_stats *stats;
+	struct graph_lcore_data *data;
+
+	data = rte_zmalloc("Graph_perf", sizeof(struct graph_lcore_data),
+			   RTE_CACHE_LINE_SIZE);
+	data->graph_id = graph_id;
+	data->done = 0;
+
+	/* Run graph worker thread function */
+	rte_eal_remote_launch(_graph_perf_wrapper, data, lcore_id);
+
+	/* Collect stats for few msecs */
+	if (rte_graph_has_stats_feature()) {
+		memset(&param, 0, sizeof(param));
+		param.f = stdout;
+		param.socket_id = SOCKET_ID_ANY;
+		param.graph_patterns = &pattern;
+		param.nb_graph_patterns = 1;
+
+		stats = rte_graph_cluster_stats_create(&param);
+		if (stats == NULL) {
+			printf("Failed to create stats\n");
+			return -ENOMEM;
+		}
+
+		rte_delay_ms(3E2);
+		rte_graph_cluster_stats_get(stats, true);
+		rte_delay_ms(1E3);
+		rte_graph_cluster_stats_get(stats, false);
+		rte_graph_cluster_stats_destroy(stats);
+	} else
+		rte_delay_ms(1E3);
+
+	data->done = 1;
+	rte_eal_wait_lcore(lcore_id);
+
+	return 0;
+}
+
+static inline void
+graph_fini(void)
+{
+	const struct rte_memzone *mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	struct test_graph_perf *graph_data;
+
+	if (mz == NULL)
+		return;
+	graph_data = mz->addr;
+
+	rte_graph_destroy(graph_data->graph_id);
+	free(graph_data->node_data);
+	rte_memzone_free(rte_memzone_lookup(TEST_GRAPH_PERF_MZ));
+}
+
+static int
+measure_perf(void)
+{
+	const struct rte_memzone *mz;
+	struct test_graph_perf *graph_data;
+
+	mz = rte_memzone_lookup(TEST_GRAPH_PERF_MZ);
+	graph_data = mz->addr;
+
+	return measure_perf_get(graph_data->graph_id);
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_1snk_brst_one(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_2src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_hr_4s_1n_1src_2snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_tree_4s_4n_1src_4snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_reverse_tree_3s_4n_1src_1snk(void)
+{
+	return measure_perf();
+}
+
+static inline int
+graph_parallel_tree_5s_4n_4src_4snk(void)
+{
+	return measure_perf();
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_hr_brst_one(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 1);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			2
+ * sink:		1
+ */
+static inline int
+graph_init_hr_multi_src(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = {
+		{100}, {100}
+	};
+	uint8_t snk_map[][1] = { {100} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	1
+ * stages:		4
+ * src:			1
+ * sink:		2
+ */
+static inline int
+graph_init_hr_multi_snk(void)
+{
+	uint8_t edge_map[][1][1] = {
+		{ {100} },
+		{ {100} },
+		{ {100} },
+		{ {100} },
+	};
+	uint8_t src_map[][1] = { {100} };
+	uint8_t snk_map[][2] = { {50, 50} };
+
+	return graph_init("graph_hr", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		4
+ * src:			1
+ * sink:		4
+ */
+static inline int
+graph_init_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 0, 0, 0},
+			{50, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{33, 33, 0, 0},
+			{34, 34, 0, 0},
+			{33, 33, 0, 0},
+			{0, 0, 0, 0}
+		},
+		{
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0},
+			{25, 25, 25, 0}
+		}
+	};
+	uint8_t src_map[][4] = { {100, 0, 0, 0} };
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		3
+ * src:			1
+ * sink:		1
+ */
+static inline int
+graph_init_reverse_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25},
+			{25, 25, 25, 25}
+		},
+		{
+			{33, 33, 33, 33},
+			{33, 33, 33, 33},
+			{34, 34, 34, 34},
+			{0, 0, 0, 0}
+		},
+		{
+			{50, 50, 50, 0},
+			{50, 50, 50, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0}
+		},
+	};
+	uint8_t src_map[][4] = { {25, 25, 25, 25} };
+	uint8_t snk_map[][1] = { {100}, {100}, {0}, {0} };
+
+	return graph_init("graph_full_split", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/* Graph Topology
+ * nodes per stage:	4
+ * stages:		5
+ * src:			4
+ * sink:		4
+ */
+static inline int
+graph_init_parallel_tree(void)
+{
+	uint8_t edge_map[][4][4] = {
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+		{
+			{100, 0, 0, 0},
+			{0, 100, 0, 0},
+			{0, 0, 100, 0},
+			{0, 0, 0, 100}
+		},
+	};
+	uint8_t src_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+	uint8_t snk_map[][4] = {
+		{100, 0, 0, 0},
+		{0, 100, 0, 0},
+		{0, 0, 100, 0},
+		{0, 0, 0, 100}
+	};
+
+	return graph_init("graph_parallel", SOURCES(src_map), SINKS(snk_map),
+			  STAGES(edge_map), NODES_PER_STAGE(edge_map), src_map,
+			  snk_map, edge_map, 0);
+}
+
+/** Graph Creation cheat sheet
+ *  edge_map -> dictates graph flow from worker stage 0 to worker stage n-1.
+ *  src_map  -> dictates source nodes enqueue percentage to worker stage 0.
+ *  snk_map  -> dictates stage n-1 enqueue percentage to sink.
+ *
+ *  Layout:
+ *  edge_map[<nb_stages>][<nodes_per_stg>][<nodes_in_nxt_stg = nodes_per_stg>]
+ *  src_map[<nb_sources>][<nodes_in_stage0 = nodes_per_stage>]
+ *  snk_map[<nodes_in_stage(n-1) = nodes_per_stage>][<nb_sinks>]
+ *
+ *  The last array dictates the percentage of received objs to enqueue to next
+ *  stage.
+ *
+ *  Note: edge_map[][0][] will always be unused as it will receive from source
+ *
+ *  Example:
+ *	Graph:
+ *	http://bit.ly/2PqbqOy
+ *	Each stage(n) connects to all nodes in the next stage in decreasing
+ *	order.
+ *	Since we can't resize the edge_map dynamically we get away by creating
+ *	dummy nodes and assigning 0 percentages.
+ *	Max nodes across all stages = 4
+ *	stages = 3
+ *	nb_src = 1
+ *	nb_snk = 1
+ *			   // Stages
+ *	edge_map[][4][4] = {
+ *		// Nodes per stage
+ *		{
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25},
+ *		    {25, 25, 25, 25}
+ *		},	// This will be unused.
+ *		{
+ *		    // Nodes enabled in current stage + prev stage enq %
+ *		    {33, 33, 33, 33},
+ *		    {33, 33, 33, 33},
+ *		    {34, 34, 34, 34},
+ *		    {0, 0, 0, 0}
+ *		},
+ *		{
+ *		    {50, 50, 50, 0},
+ *		    {50, 50, 50, 0},
+ *		    {0, 0, 0, 0},
+ *		    {0, 0, 0, 0}
+ *		},
+ *	};
+ *	Above, each stage tells how much it should receive from previous except
+ *	from stage_0.
+ *
+ *	src_map[][4] = { {25, 25, 25, 25} };
+ *	Here, we tell each source the % it has to send to stage_0 nodes. In
+ *	case we want 2 source node we can declare as
+ *	src_map[][4] = { {25, 25, 25, 25}, {25, 25, 25, 25} };
+ *
+ *	snk_map[][1] = { {100}, {100}, {0}, {0} }
+ *	Here, we tell stage - 1 nodes how much to enqueue to sink_0.
+ *	If we have 2 sinks we can do as follows
+ *	snk_map[][2] = { {50, 50}, {50, 50}, {0, 0}, {0, 0} }
+ */
+
+static struct unit_test_suite graph_perf_testsuite = {
+	.suite_name = "Graph library performance test suite",
+	.setup = graph_perf_setup,
+	.teardown = graph_perf_teardown,
+	.unit_test_cases = {
+		TEST_CASE_ST(graph_init_hr, graph_fini,
+			     graph_hr_4s_1n_1src_1snk),
+		TEST_CASE_ST(graph_init_hr_brst_one, graph_fini,
+			     graph_hr_4s_1n_1src_1snk_brst_one),
+		TEST_CASE_ST(graph_init_hr_multi_src, graph_fini,
+			     graph_hr_4s_1n_2src_1snk),
+		TEST_CASE_ST(graph_init_hr_multi_snk, graph_fini,
+			     graph_hr_4s_1n_1src_2snk),
+		TEST_CASE_ST(graph_init_tree, graph_fini,
+			     graph_tree_4s_4n_1src_4snk),
+		TEST_CASE_ST(graph_init_reverse_tree, graph_fini,
+			     graph_reverse_tree_3s_4n_1src_1snk),
+		TEST_CASE_ST(graph_init_parallel_tree, graph_fini,
+			     graph_parallel_tree_5s_4n_4src_4snk),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+test_graph_perf_func(void)
+{
+	return unit_test_suite_runner(&graph_perf_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_perf_autotest, test_graph_perf_func);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 15/29] node: add log infra and null node
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (13 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 14/29] graph: add performance testcase jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 16/29] node: add ethdev Rx node jerinj
                           ` (15 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic,
	Nithin Dabilpuram, Pavan Nikhilesh, Bruce Richardson
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add log infra for node specific logging.
Also, add null rte_node that just ignores all the objects
directed to it.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 MAINTAINERS                          |  5 +++++
 app/test/meson.build                 |  7 +++++--
 config/common_base                   |  5 +++++
 doc/api/doxy-api.conf.in             |  1 +
 lib/Makefile                         |  3 +++
 lib/librte_node/Makefile             | 22 ++++++++++++++++++++++
 lib/librte_node/log.c                | 14 ++++++++++++++
 lib/librte_node/meson.build          |  8 ++++++++
 lib/librte_node/node_private.h       | 22 ++++++++++++++++++++++
 lib/librte_node/null.c               | 23 +++++++++++++++++++++++
 lib/librte_node/rte_node_version.map |  6 ++++++
 lib/meson.build                      |  5 ++++-
 meson.build                          |  1 +
 mk/rte.app.mk                        |  1 +
 14 files changed, 120 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/Makefile
 create mode 100644 lib/librte_node/log.c
 create mode 100644 lib/librte_node/meson.build
 create mode 100644 lib/librte_node/node_private.h
 create mode 100644 lib/librte_node/null.c
 create mode 100644 lib/librte_node/rte_node_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index aa40cc92b..55fb9bbb0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1467,6 +1467,11 @@ M: Jerin Jacob <jerinj@marvell.com>
 M: Kiran Kumar K <kirankumark@marvell.com>
 F: lib/librte_graph/
 
+Nodes - EXPERIMENTAL
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+M: Pavan Nikhilesh <pbhagavatula@marvell.com>
+F: lib/librte_node/
+
 
 Test Applications
 -----------------
diff --git a/app/test/meson.build b/app/test/meson.build
index 9006cc074..728d20c1f 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -154,7 +154,8 @@ test_deps = ['acl',
 	'ring',
 	'stack',
 	'timer',
-	'graph'
+	'graph',
+	'node'
 ]
 
 # Each test is marked with flag true/false
@@ -390,13 +391,15 @@ endforeach
 test_dep_objs += cc.find_library('execinfo', required: false)
 
 link_libs = []
+link_nodes = []
 if get_option('default_library') == 'static'
 	link_libs = dpdk_drivers
+	link_nodes = dpdk_graph_nodes
 endif
 
 dpdk_test = executable('dpdk-test',
 	test_sources,
-	link_whole: link_libs,
+	link_whole: link_libs + link_nodes,
 	dependencies: test_dep_objs,
 	c_args: [cflags, '-DALLOW_EXPERIMENTAL_API'],
 	install_rpath: driver_install_path,
diff --git a/config/common_base b/config/common_base
index 32f982136..1ed5187dc 100644
--- a/config/common_base
+++ b/config/common_base
@@ -1081,6 +1081,11 @@ CONFIG_RTE_LIBRTE_GRAPH=y
 CONFIG_RTE_GRAPH_BURST_SIZE=256
 CONFIG_RTE_LIBRTE_GRAPH_STATS=y
 
+#
+# Compile librte_node
+#
+CONFIG_RTE_LIBRTE_NODE=y
+
 #
 # Compile the test application
 #
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index e3b7f54f8..92122125a 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -49,6 +49,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/librte_mempool \
                           @TOPDIR@/lib/librte_meter \
                           @TOPDIR@/lib/librte_metrics \
+                          @TOPDIR@/lib/librte_node \
                           @TOPDIR@/lib/librte_net \
                           @TOPDIR@/lib/librte_pci \
                           @TOPDIR@/lib/librte_pdump \
diff --git a/lib/Makefile b/lib/Makefile
index 1f572b659..50d61a338 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -122,6 +122,9 @@ DEPDIRS-librte_rcu := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_GRAPH) += librte_graph
 DEPDIRS-librte_graph := librte_eal
 
+DIRS-$(CONFIG_RTE_LIBRTE_NODE) += librte_node
+DEPDIRS-librte_node := librte_graph librte_lpm librte_ethdev librte_mbuf
+
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
 endif
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
new file mode 100644
index 000000000..dbc8e1d44
--- /dev/null
+++ b/lib/librte_node/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+#
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_node.a
+
+CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
+LDLIBS += -lrte_eal -lrte_graph
+
+EXPORT_MAP := rte_node_version.map
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/log.c b/lib/librte_node/log.c
new file mode 100644
index 000000000..f035f91e8
--- /dev/null
+++ b/lib/librte_node/log.c
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include "node_private.h"
+
+int rte_node_logtype;
+
+RTE_INIT(rte_node_init_log)
+{
+	rte_node_logtype = rte_log_register("lib.node");
+	if (rte_node_logtype >= 0)
+		rte_log_set_level(rte_node_logtype, RTE_LOG_INFO);
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
new file mode 100644
index 000000000..a97813ad4
--- /dev/null
+++ b/lib/librte_node/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+sources = files('null.c', 'log.c')
+allow_experimental_apis = true
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
+deps += ['graph']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
new file mode 100644
index 000000000..f30902a94
--- /dev/null
+++ b/lib/librte_node/node_private.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __NODE_PRIVATE_H__
+#define __NODE_PRIVATE_H__
+
+#include <rte_common.h>
+#include <rte_log.h>
+
+extern int rte_node_logtype;
+#define NODE_LOG(level, node_name, ...)                                        \
+	rte_log(RTE_LOG_##level, rte_node_logtype,                             \
+		RTE_FMT("NODE %s: %s():%u " RTE_FMT_HEAD(__VA_ARGS__, ) "\n",  \
+			node_name, __func__, __LINE__,                         \
+			RTE_FMT_TAIL(__VA_ARGS__, )))
+
+#define node_err(node_name, ...) NODE_LOG(ERR, node_name, __VA_ARGS__)
+#define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
+#define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
+
+#endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/null.c b/lib/librte_node/null.c
new file mode 100644
index 000000000..c7cd8b6df
--- /dev/null
+++ b/lib/librte_node/null.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_graph.h>
+
+static uint16_t
+null(struct rte_graph *graph, struct rte_node *node, void **objs,
+	uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(objs);
+	RTE_SET_USED(graph);
+
+	return nb_objs;
+}
+
+static struct rte_node_register null_node = {
+	.name = "null",
+	.process = null,
+};
+
+RTE_NODE_REGISTER(null_node);
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
new file mode 100644
index 000000000..f87163bb9
--- /dev/null
+++ b/lib/librte_node/rte_node_version.map
@@ -0,0 +1,6 @@
+EXPERIMENTAL {
+	global:
+
+	rte_node_logtype;
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index c43d86bb9..147129b0b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -30,7 +30,7 @@ libraries = [
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
-	'flow_classify', 'bpf', 'graph', 'telemetry']
+	'flow_classify', 'bpf', 'graph', 'node', 'telemetry']
 
 if is_windows
 	libraries = ['kvargs','eal'] # only supported libraries for windows
@@ -186,6 +186,9 @@ foreach l:libraries
 
 			dpdk_libraries = [shared_lib] + dpdk_libraries
 			dpdk_static_libraries = [static_lib] + dpdk_static_libraries
+			if libname == 'rte_node'
+				dpdk_graph_nodes = [static_lib]
+			endif
 		endif # sources.length() > 0
 
 		set_variable('shared_rte_' + name, shared_dep)
diff --git a/meson.build b/meson.build
index d36580438..269ba9614 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@ cc = meson.get_compiler('c')
 dpdk_conf = configuration_data()
 dpdk_libraries = []
 dpdk_static_libraries = []
+dpdk_graph_nodes = []
 dpdk_driver_classes = []
 dpdk_drivers = []
 dpdk_extra_ldflags = []
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index b1195f09a..68d7806a4 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -99,6 +99,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RCU)            += -lrte_rcu
 _LDLIBS-$(CONFIG_RTE_LIBRTE_GRAPH)          += -lrte_graph
+_LDLIBS-$(CONFIG_RTE_LIBRTE_NODE)           += -lrte_node
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 16/29] node: add ethdev Rx node
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (14 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 15/29] node: add log infra and null node jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 17/29] node: add ethdev Tx node jerinj
                           ` (14 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add source rte_node ethdev_rx process function and register
it. This node is a source node that will be called periodically
and when called, performs rte_eth_rx_burst() on a specific
(port, queue) pair and enqueue them as stream of objects to
next node.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |   3 +-
 lib/librte_node/ethdev_rx.c      | 221 +++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_rx_priv.h |  81 +++++++++++
 lib/librte_node/meson.build      |   4 +-
 4 files changed, 306 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_rx.c
 create mode 100644 lib/librte_node/ethdev_rx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index dbc8e1d44..314149385 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,12 +11,13 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph
+LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
 # all source are stored in SRCS-y
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_rx.c b/lib/librte_node/ethdev_rx.c
new file mode 100644
index 000000000..5cc736598
--- /dev/null
+++ b/lib/librte_node/ethdev_rx.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_rx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_rx_node_main ethdev_rx_main;
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process_inline(struct rte_graph *graph, struct rte_node *node,
+			      uint16_t port, uint16_t queue)
+{
+	uint16_t count, next_index = ETHDEV_RX_NEXT_IP4_LOOKUP;
+
+	/* Get pkts from port */
+	count = rte_eth_rx_burst(port, queue, (struct rte_mbuf **)node->objs,
+				 RTE_GRAPH_BURST_SIZE);
+
+	if (!count)
+		return 0;
+	node->idx = count;
+	/* Enqueue to next node */
+	rte_node_next_stream_move(graph, node, next_index);
+
+	return count;
+}
+
+static __rte_always_inline uint16_t
+ethdev_rx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t cnt)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	uint16_t n_pkts = 0;
+
+	RTE_SET_USED(objs);
+	RTE_SET_USED(cnt);
+
+	n_pkts = ethdev_rx_node_process_inline(graph, node, ctx->port_id,
+					       ctx->queue_id);
+	return n_pkts;
+}
+
+static inline uint32_t
+l3_ptype(uint16_t etype, uint32_t ptype)
+{
+	ptype = ptype & ~RTE_PTYPE_L3_MASK;
+	if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
+		ptype |= RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
+	else if (etype == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6))
+		ptype |= RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
+	return ptype;
+}
+
+/* Callback for soft ptype parsing */
+static uint16_t
+eth_pkt_parse_cb(uint16_t port, uint16_t queue, struct rte_mbuf **mbufs,
+		 uint16_t nb_pkts, uint16_t max_pkts, void *user_param)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3;
+	struct rte_ether_hdr *eth_hdr;
+	uint16_t etype, n_left;
+	struct rte_mbuf **pkts;
+
+	RTE_SET_USED(port);
+	RTE_SET_USED(queue);
+	RTE_SET_USED(max_pkts);
+	RTE_SET_USED(user_param);
+
+	pkts = mbufs;
+	n_left = nb_pkts;
+	while (n_left >= 12) {
+
+		/* Prefetch next-next mbufs */
+		rte_prefetch0(pkts[8]);
+		rte_prefetch0(pkts[9]);
+		rte_prefetch0(pkts[10]);
+		rte_prefetch0(pkts[11]);
+
+		/* Prefetch next mbuf data */
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[4], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[5], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[6], struct rte_ether_hdr *));
+		rte_prefetch0(
+			rte_pktmbuf_mtod(pkts[7], struct rte_ether_hdr *));
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+		pkts += 4;
+		n_left -= 4;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf1 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf1, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf1->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf2 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf2, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf2->packet_type = l3_ptype(etype, 0);
+
+		/* Extract ptype of mbuf3 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf3, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf3->packet_type = l3_ptype(etype, 0);
+	}
+
+	while (n_left > 0) {
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left -= 1;
+
+		/* Extract ptype of mbuf0 */
+		eth_hdr = rte_pktmbuf_mtod(mbuf0, struct rte_ether_hdr *);
+		etype = eth_hdr->ether_type;
+		mbuf0->packet_type = l3_ptype(etype, 0);
+	}
+
+	return nb_pkts;
+}
+
+#define MAX_PTYPES 16
+static int
+ethdev_ptype_setup(uint16_t port, uint16_t queue)
+{
+	uint8_t l3_ipv4 = 0, l3_ipv6 = 0;
+	uint32_t ptypes[MAX_PTYPES];
+	int i, rc;
+
+	/* Check IPv4 & IPv6 ptype support */
+	rc = rte_eth_dev_get_supported_ptypes(port, RTE_PTYPE_L3_MASK, ptypes,
+					      MAX_PTYPES);
+	for (i = 0; i < rc; i++) {
+		if (ptypes[i] & RTE_PTYPE_L3_IPV4)
+			l3_ipv4 = 1;
+		if (ptypes[i] & RTE_PTYPE_L3_IPV6)
+			l3_ipv6 = 1;
+	}
+
+	if (!l3_ipv4 || !l3_ipv6) {
+		node_info("ethdev_rx",
+			  "Enabling ptype callback for required ptypes on port %u\n",
+			  port);
+
+		if (!rte_eth_add_rx_callback(port, queue, eth_pkt_parse_cb,
+					     NULL)) {
+			node_err("ethdev_rx",
+				 "Failed to add rx ptype cb: port=%d, queue=%d\n",
+				 port, queue);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int
+ethdev_rx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_rx_node_ctx_t *ctx = (ethdev_rx_node_ctx_t *)node->ctx;
+	ethdev_rx_node_elem_t *elem = ethdev_rx_main.head;
+
+	RTE_SET_USED(graph);
+
+	while (elem) {
+		if (elem->nid == node->id) {
+			/* Update node specific context */
+			memcpy(ctx, &elem->ctx, sizeof(ethdev_rx_node_ctx_t));
+			break;
+		}
+		elem = elem->next;
+	}
+
+	RTE_VERIFY(elem != NULL);
+
+	/* Check and setup ptype */
+	return ethdev_ptype_setup(ctx->port_id, ctx->queue_id);
+}
+
+struct ethdev_rx_node_main *
+ethdev_rx_get_node_data_get(void)
+{
+	return &ethdev_rx_main;
+}
+
+static struct rte_node_register ethdev_rx_node_base = {
+	.process = ethdev_rx_node_process,
+	.flags = RTE_NODE_SOURCE_F,
+	.name = "ethdev_rx",
+
+	.init = ethdev_rx_node_init,
+
+	.nb_edges = ETHDEV_RX_NEXT_MAX,
+	.next_nodes = {[ETHDEV_RX_NEXT_IP4_LOOKUP] = "ip4_lookup"},
+};
+
+struct rte_node_register *
+ethdev_rx_node_get(void)
+{
+	return &ethdev_rx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_rx_node_base);
diff --git a/lib/librte_node/ethdev_rx_priv.h b/lib/librte_node/ethdev_rx_priv.h
new file mode 100644
index 000000000..2d7195a36
--- /dev/null
+++ b/lib/librte_node/ethdev_rx_priv.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_RX_PRIV_H__
+#define __INCLUDE_ETHDEV_RX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+struct ethdev_rx_node_elem;
+struct ethdev_rx_node_ctx;
+typedef struct ethdev_rx_node_elem ethdev_rx_node_elem_t;
+typedef struct ethdev_rx_node_ctx ethdev_rx_node_ctx_t;
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node context structure.
+ */
+struct ethdev_rx_node_ctx {
+	uint16_t port_id;  /**< Port identifier of the Rx node. */
+	uint16_t queue_id; /**< Queue identifier of the Rx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet device Rx node list element structure.
+ */
+struct ethdev_rx_node_elem {
+	struct ethdev_rx_node_elem *next;
+	/**< Pointer to the next Rx node element. */
+	struct ethdev_rx_node_ctx ctx;
+	/**< Rx node context. */
+	rte_node_t nid;
+	/**< Node identifier of the Rx node. */
+};
+
+enum ethdev_rx_next_nodes {
+	ETHDEV_RX_NEXT_IP4_LOOKUP,
+	ETHDEV_RX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Rx node main structure.
+ */
+struct ethdev_rx_node_main {
+	ethdev_rx_node_elem_t *head;
+	/**< Pointer to the head Rx node element. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Rx node data.
+ */
+struct ethdev_rx_node_main *ethdev_rx_get_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Rx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Rx node.
+ */
+struct rte_node_register *ethdev_rx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_RX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index a97813ad4..94caa6c23 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph']
+deps += ['graph', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 17/29] node: add ethdev Tx node
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (15 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 16/29] node: add ethdev Rx node jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
                           ` (13 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add rte_node ethdev_tx process function and register it to
graph infra. This node has a specific (port, tx-queue) as context
and it enqueue's all the packets received to that specific queue pair.
When rte_eth_tx_burst() i.e enqueue to queue pair fails, packets
are forwarded to pkt_drop node to be free'd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile         |  3 +-
 lib/librte_node/ethdev_tx.c      | 86 ++++++++++++++++++++++++++++++++
 lib/librte_node/ethdev_tx_priv.h | 62 +++++++++++++++++++++++
 lib/librte_node/meson.build      |  4 +-
 4 files changed, 152 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_tx.c
 create mode 100644 lib/librte_node/ethdev_tx_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 314149385..7428f6c43 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
 
 EXPORT_MAP := rte_node_version.map
 
@@ -19,5 +19,6 @@ EXPORT_MAP := rte_node_version.map
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_tx.c b/lib/librte_node/ethdev_tx.c
new file mode 100644
index 000000000..075149089
--- /dev/null
+++ b/lib/librte_node/ethdev_tx.c
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "ethdev_tx_priv.h"
+
+static struct ethdev_tx_node_main ethdev_tx_main;
+
+static uint16_t
+ethdev_tx_node_process(struct rte_graph *graph, struct rte_node *node,
+		       void **objs, uint16_t nb_objs)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint16_t port, queue;
+	uint16_t count;
+
+	/* Get Tx port id */
+	port = ctx->port;
+	queue = ctx->queue;
+
+	count = rte_eth_tx_burst(port, queue, (struct rte_mbuf **)objs,
+				 nb_objs);
+
+	/* Redirect unsent pkts to drop node */
+	if (count != nb_objs) {
+		rte_node_enqueue(graph, node, ETHDEV_TX_NEXT_PKT_DROP,
+				 &objs[count], nb_objs - count);
+	}
+
+	return count;
+}
+
+static int
+ethdev_tx_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	ethdev_tx_node_ctx_t *ctx = (ethdev_tx_node_ctx_t *)node->ctx;
+	uint64_t port_id = RTE_MAX_ETHPORTS;
+	int i;
+
+	/* Find our port id */
+	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
+		if (ethdev_tx_main.nodes[i] == node->id) {
+			port_id = i;
+			break;
+		}
+	}
+	RTE_VERIFY(port_id < RTE_MAX_ETHPORTS);
+
+	/* Update port and queue */
+	ctx->port = port_id;
+	ctx->queue = graph->id;
+
+	return 0;
+}
+
+struct ethdev_tx_node_main *
+ethdev_tx_node_data_get(void)
+{
+	return &ethdev_tx_main;
+}
+
+static struct rte_node_register ethdev_tx_node_base = {
+	.process = ethdev_tx_node_process,
+	.name = "ethdev_tx",
+
+	.init = ethdev_tx_node_init,
+
+	.nb_edges = ETHDEV_TX_NEXT_MAX,
+	.next_nodes = {
+		[ETHDEV_TX_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+struct rte_node_register *
+ethdev_tx_node_get(void)
+{
+	return &ethdev_tx_node_base;
+}
+
+RTE_NODE_REGISTER(ethdev_tx_node_base);
diff --git a/lib/librte_node/ethdev_tx_priv.h b/lib/librte_node/ethdev_tx_priv.h
new file mode 100644
index 000000000..586bff44a
--- /dev/null
+++ b/lib/librte_node/ethdev_tx_priv.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_ETHDEV_TX_PRIV_H__
+#define __INCLUDE_ETHDEV_TX_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ethdev_tx_node_ctx;
+typedef struct ethdev_tx_node_ctx ethdev_tx_node_ctx_t;
+
+enum ethdev_tx_next_nodes {
+	ETHDEV_TX_NEXT_PKT_DROP,
+	ETHDEV_TX_NEXT_MAX,
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node context structure.
+ */
+struct ethdev_tx_node_ctx {
+	uint16_t port;	/**< Port identifier of the Ethernet Tx node. */
+	uint16_t queue; /**< Queue identifier of the Ethernet Tx node. */
+};
+
+/**
+ * @internal
+ *
+ * Ethernet Tx node main structure.
+ */
+struct ethdev_tx_node_main {
+	uint32_t nodes[RTE_MAX_ETHPORTS]; /**< Tx nodes for each ethdev port. */
+};
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node data.
+ *
+ * @return
+ *   Pointer to Ethernet Tx node data.
+ */
+struct ethdev_tx_node_main *ethdev_tx_node_data_get(void);
+
+/**
+ * @internal
+ *
+ * Get the Ethernet Tx node.
+ *
+ * @retrun
+ *   Pointer to the Ethernet Tx node.
+ */
+struct rte_node_register *ethdev_tx_node_get(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_ETHDEV_TX_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 94caa6c23..505c76abd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,8 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'ethdev']
+deps += ['graph', 'mbuf', 'ethdev']
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 18/29] node: add ethdev Rx and Tx node ctrl API
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (16 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 17/29] node: add ethdev Tx node jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 19/29] node: add generic ipv4 lookup node jerinj
                           ` (12 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ctrl api to setup ethdev_rx and ethdev_tx node.
This ctrl api clones 'N' number of ethdev_rx and ethdev_tx
nodes with specific (port, queue) pairs updated in their context.
All the ethdev ports and queues are setup before this api
is called.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 doc/api/doxy-api-index.md            |  2 +
 lib/librte_node/Makefile             |  6 +-
 lib/librte_node/ethdev_ctrl.c        | 99 ++++++++++++++++++++++++++++
 lib/librte_node/meson.build          |  5 +-
 lib/librte_node/node_private.h       | 57 ++++++++++++++++
 lib/librte_node/rte_node_eth_api.h   | 64 ++++++++++++++++++
 lib/librte_node/rte_node_version.map |  1 +
 7 files changed, 231 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ethdev_ctrl.c
 create mode 100644 lib/librte_node/rte_node_eth_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index fd2ff64d7..1784674df 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -161,6 +161,8 @@ The public API headers are grouped by topics:
     [table_action]     (@ref rte_table_action.h)
   * [graph]            (@ref rte_graph.h):
     [graph_worker]     (@ref rte_graph_worker.h)
+  * graph_nodes:
+    [eth_node]         (@ref rte_node_eth_api.h),
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index 7428f6c43..ea5fa77f7 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -20,5 +20,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += null.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+
+# install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
new file mode 100644
index 000000000..599d20b0b
--- /dev/null
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+
+#include "rte_node_eth_api.h"
+
+#include "ethdev_rx_priv.h"
+#include "ethdev_tx_priv.h"
+#include "node_private.h"
+
+static struct ethdev_ctrl {
+	uint16_t nb_graphs;
+} ctrl;
+
+int
+rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
+		    uint16_t nb_graphs)
+{
+	struct ethdev_tx_node_main *tx_node_data;
+	uint16_t tx_q_used, rx_q_used, port_id;
+	struct rte_node_register *tx_node;
+	char name[RTE_NODE_NAMESIZE];
+	struct rte_mempool *mp;
+	uint32_t id;
+	int i, j;
+
+	tx_node_data = ethdev_tx_node_data_get();
+	tx_node = ethdev_tx_node_get();
+	for (i = 0; i < nb_confs; i++) {
+		port_id = conf[i].port_id;
+
+		if (!rte_eth_dev_is_valid_port(port_id))
+			return -EINVAL;
+
+		/* Check for mbuf minimum private size requirement */
+		for (j = 0; j < conf[i].mp_count; j++) {
+			mp = conf[i].mp[j];
+			if (!mp)
+				continue;
+			/* Check for minimum private space */
+			if (rte_pktmbuf_priv_size(mp) < NODE_MBUF_PRIV2_SIZE) {
+				node_err("ethdev",
+					 "Minimum mbuf priv size requirement not met by mp %s",
+					 mp->name);
+				return -EINVAL;
+			}
+		}
+
+		rx_q_used = conf[i].num_rx_queues;
+		tx_q_used = conf[i].num_tx_queues;
+		/* Check if we have a txq for each worker */
+		if (tx_q_used < nb_graphs)
+			return -EINVAL;
+
+		/* Create node for each rx port queue pair */
+		for (j = 0; j < rx_q_used; j++) {
+			struct ethdev_rx_node_main *rx_node_data;
+			struct rte_node_register *rx_node;
+			ethdev_rx_node_elem_t *elem;
+
+			rx_node_data = ethdev_rx_get_node_data_get();
+			rx_node = ethdev_rx_node_get();
+			snprintf(name, sizeof(name), "%u-%u", port_id, j);
+			/* Clone a new rx node with same edges as parent */
+			id = rte_node_clone(rx_node->id, name);
+			if (id == RTE_NODE_ID_INVALID)
+				return -EIO;
+
+			/* Add it to list of ethdev rx nodes for lookup */
+			elem = malloc(sizeof(ethdev_rx_node_elem_t));
+			memset(elem, 0, sizeof(ethdev_rx_node_elem_t));
+			elem->ctx.port_id = port_id;
+			elem->ctx.queue_id = j;
+			elem->nid = id;
+			elem->next = rx_node_data->head;
+			rx_node_data->head = elem;
+
+			node_dbg("ethdev", "Rx node %s-%s: is at %u",
+				 rx_node->name, name, id);
+		}
+
+		/* Create a per port tx node from base node */
+		snprintf(name, sizeof(name), "%u", port_id);
+		/* Clone a new node with same edges as parent */
+		id = rte_node_clone(tx_node->id, name);
+		tx_node_data->nodes[port_id] = id;
+
+		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
+			 name, id);
+	}
+
+	ctrl.nb_graphs = nb_graphs;
+	return 0;
+}
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 505c76abd..af2eb2d91 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
+headers = files('rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
-deps += ['graph', 'mbuf', 'ethdev']
+deps += ['graph', 'mbuf', 'lpm', 'ethdev', 'mempool', 'cryptodev']
diff --git a/lib/librte_node/node_private.h b/lib/librte_node/node_private.h
index f30902a94..975b9aa45 100644
--- a/lib/librte_node/node_private.h
+++ b/lib/librte_node/node_private.h
@@ -7,6 +7,7 @@
 
 #include <rte_common.h>
 #include <rte_log.h>
+#include <rte_mbuf.h>
 
 extern int rte_node_logtype;
 #define NODE_LOG(level, node_name, ...)                                        \
@@ -19,4 +20,60 @@ extern int rte_node_logtype;
 #define node_info(node_name, ...) NODE_LOG(INFO, node_name, __VA_ARGS__)
 #define node_dbg(node_name, ...) NODE_LOG(DEBUG, node_name, __VA_ARGS__)
 
+/**
+ *
+ * Node mbuf private data to store next hop, ttl and checksum.
+ */
+struct node_mbuf_priv1 {
+	union {
+		/* IP4 rewrite */
+		struct {
+			uint16_t nh;
+			uint16_t ttl;
+			uint32_t cksum;
+		};
+
+		uint64_t u;
+	};
+};
+
+/**
+ * Node mbuf private area 2.
+ */
+struct node_mbuf_priv2 {
+	uint64_t priv_data;
+} __rte_cache_aligned;
+
+#define NODE_MBUF_PRIV2_SIZE sizeof(struct node_mbuf_priv2)
+
+/**
+ * Get mbuf_priv1 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv1.
+ */
+static __rte_always_inline struct node_mbuf_priv1 *
+node_mbuf_priv1(struct rte_mbuf *m)
+{
+	return (struct node_mbuf_priv1 *)&m->udata64;
+}
+
+/**
+ * Get mbuf_priv2 pointer from rte_mbuf.
+ *
+ * @param
+ *   Pointer to the rte_mbuf.
+ *
+ * @return
+ *   Pointer to the mbuf_priv2.
+ */
+static __rte_always_inline struct node_mbuf_priv2 *
+node_mbuf_priv2(struct rte_mbuf *m)
+{
+	return (struct node_mbuf_priv2 *)rte_mbuf_to_priv(m);
+}
+
 #endif /* __NODE_PRIVATE_H__ */
diff --git a/lib/librte_node/rte_node_eth_api.h b/lib/librte_node/rte_node_eth_api.h
new file mode 100644
index 000000000..e9a53afe5
--- /dev/null
+++ b/lib/librte_node/rte_node_eth_api.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_ETH_API_H__
+#define __INCLUDE_RTE_NODE_ETH_API_H__
+
+/**
+ * @file rte_node_eth_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to setup ethdev_rx and ethdev_tx nodes
+ * and its queue associations.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_mempool.h>
+
+/**
+ * Port config for ethdev_rx and ethdev_tx node.
+ */
+struct rte_node_ethdev_config {
+	uint16_t port_id;
+	/**< Port identifier */
+	uint16_t num_rx_queues;
+	/**< Number of Rx queues. */
+	uint16_t num_tx_queues;
+	/**< Number of Tx queues. */
+	struct rte_mempool **mp;
+	/**< Array of mempools associated to Rx queue. */
+	uint16_t mp_count;
+	/**< Size of mp array. */
+};
+
+/**
+ * Initializes ethdev nodes.
+ *
+ * @param cfg
+ *   Array of ethdev config that identifies which port's
+ *   ethdev_rx and ethdev_tx nodes need to be created
+ *   and queue association.
+ * @param cnt
+ *   Size of cfg array.
+ * @param nb_graphs
+ *   Number of graphs that will be used.
+ *
+ * @return
+ *   0 on successful initialization, negative otherwise.
+ */
+__rte_experimental
+int rte_node_eth_config(struct rte_node_ethdev_config *cfg,
+			uint16_t cnt, uint16_t nb_graphs);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_ETH_API_H__ */
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index f87163bb9..c6c71bd02 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -1,6 +1,7 @@
 EXPERIMENTAL {
 	global:
 
+	rte_node_eth_config;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 19/29] node: add generic ipv4 lookup node
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (17 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 20/29] node: ipv4 lookup for arm64 jerinj
                           ` (11 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic, Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup node.
This node performs LPM lookup using simple RTE_LPM API on every packet
received and forwards it to a next node that is identified by lookup
result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 doc/api/doxy-api-index.md          |   1 +
 lib/librte_node/Makefile           |   4 +-
 lib/librte_node/ip4_lookup.c       | 128 +++++++++++++++++++++++++++++
 lib/librte_node/meson.build        |   5 +-
 lib/librte_node/rte_node_ip4_api.h |  40 +++++++++
 5 files changed, 175 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_node/ip4_lookup.c
 create mode 100644 lib/librte_node/rte_node_ip4_api.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 1784674df..3908fddde 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -163,6 +163,7 @@ The public API headers are grouped by topics:
     [graph_worker]     (@ref rte_graph_worker.h)
   * graph_nodes:
     [eth_node]         (@ref rte_node_eth_api.h),
+    [ip4_node]         (@ref rte_node_ip4_api.h)
 
 - **basic**:
   [approx fraction]    (@ref rte_approx.h),
diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ea5fa77f7..ad3f2e349 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -11,7 +11,7 @@ CFLAGS += -O3 -DALLOW_EXPERIMENTAL_API
 CFLAGS += $(WERROR_FLAGS)
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 CFLAGS += -fno-strict-aliasing
-LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_ethdev -lrte_mempool
+LDLIBS += -lrte_eal -lrte_graph -lrte_mbuf -lrte_lpm -lrte_ethdev -lrte_mempool
 
 EXPORT_MAP := rte_node_version.map
 
@@ -21,8 +21,10 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += log.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 
 # install header files
+SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_eth_api.h
 
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
new file mode 100644
index 000000000..988bc5e0c
--- /dev/null
+++ b/lib/librte_node/ip4_lookup.c
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_lpm.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "node_private.h"
+
+#define IPV4_L3FWD_LPM_MAX_RULES 1024
+#define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
+
+/* IP4 Lookup global data struct */
+struct ip4_lookup_node_main {
+	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
+};
+
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	struct rte_mbuf *mbuf;
+	rte_edge_t next_index;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	int i, rc;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+	from = objs;
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	for (i = 0; i < nb_objs; i++) {
+		uint32_t next_hop;
+		uint16_t next;
+
+		mbuf = (struct rte_mbuf *)objs[i];
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
+				sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		node_mbuf_priv1(mbuf)->cksum = ipv4_hdr->hdr_checksum;
+		node_mbuf_priv1(mbuf)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		node_mbuf_priv1(mbuf)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next = (uint16_t)next_hop;
+
+		if (unlikely(next_index != next)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+static int
+ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+
+	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_lookup_node = {
+	.process = ip4_lookup_node_process,
+	.name = "ip4_lookup",
+
+	.init = ip4_lookup_node_init,
+
+	.nb_edges = RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	.next_nodes = {
+		[RTE_NODE_IP4_LOOKUP_NEXT_REWRITE] = "ip4_rewrite",
+		[RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(ip4_lookup_node);
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index af2eb2d91..702d21a24 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -1,8 +1,9 @@
 # SPDX-License-Identifier: BSD-3-Clause
 # Copyright(C) 2020 Marvell International Ltd.
 
-sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ethdev_ctrl.c')
-headers = files('rte_node_eth_api.h')
+sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
+		'ethdev_ctrl.c')
+headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
 cflags += '-fno-strict-aliasing'
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
new file mode 100644
index 000000000..0b9ba48a2
--- /dev/null
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_RTE_NODE_IP4_API_H__
+#define __INCLUDE_RTE_NODE_IP4_API_H__
+
+/**
+ * @file rte_node_ip4_api.h
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * This API allows to do control path functions of ip4_* nodes
+ * like ip4_lookup, ip4_rewrite.
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+/**
+ * IP4 lookup next nodes.
+ */
+enum rte_node_ip4_lookup_next {
+	RTE_NODE_IP4_LOOKUP_NEXT_REWRITE,
+	/**< Rewrite node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP,
+	/**< Packet drop node. */
+	RTE_NODE_IP4_LOOKUP_NEXT_MAX,
+	/**< Number of next nodes of lookup node. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_RTE_NODE_IP4_API_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 20/29] node: ipv4 lookup for arm64
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (18 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 19/29] node: add generic ipv4 lookup node jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-05-12  9:31           ` David Marchand
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 21/29] node: ipv4 lookup for x86 jerinj
                           ` (10 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add arm64 specific IPv4 lookup process function
for ip4_lookup node. This node performs LPM lookup
on every packet received and forwards it to a next
node that is identified by lookup result.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 lib/librte_node/ip4_lookup.c      |   6 +
 lib/librte_node/ip4_lookup_neon.h | 238 ++++++++++++++++++++++++++++++
 2 files changed, 244 insertions(+)
 create mode 100644 lib/librte_node/ip4_lookup_neon.h

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index 988bc5e0c..8ec4f0626 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -28,6 +28,10 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+#if defined(RTE_MACHINE_CPUFLAG_NEON)
+#include "ip4_lookup_neon.h"
+#else
+
 static uint16_t
 ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 			void **objs, uint16_t nb_objs)
@@ -101,6 +105,8 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 	return nb_objs;
 }
 
+#endif
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
diff --git a/lib/librte_node/ip4_lookup_neon.h b/lib/librte_node/ip4_lookup_neon.h
new file mode 100644
index 000000000..bb3150ff2
--- /dev/null
+++ b/lib/librte_node/ip4_lookup_neon.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_IP4_LOOKUP_NEON_H__
+#define __INCLUDE_IP4_LOOKUP_NEON_H__
+
+/* ARM64 NEON */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	rte_edge_t next_index;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t result;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int32x4_t dip;
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+#define OBJS_PER_CLINE (RTE_CACHE_LINE_SIZE / sizeof(void *))
+	for (i = OBJS_PER_CLINE; i < RTE_GRAPH_BURST_SIZE; i += OBJS_PER_CLINE)
+		rte_prefetch0(&objs[i]);
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+#if RTE_GRAPH_BURST_SIZE > 64
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+#endif
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 0);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[1] = ipv4_hdr->time_to_live;
+		priv01.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 1);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv01.u16[5] = ipv4_hdr->time_to_live;
+		priv01.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 2);
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[1] = ipv4_hdr->time_to_live;
+		priv23.u32[1] = ipv4_hdr->hdr_checksum;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		dip = vsetq_lane_s32(ipv4_hdr->dst_addr, dip, 3);
+
+		dip = vreinterpretq_s32_u8(
+			vrev32q_u8(vreinterpretq_u8_s32(dip)));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		priv23.u16[5] = ipv4_hdr->time_to_live;
+		priv23.u32[3] = ipv4_hdr->hdr_checksum;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, result.u32, drop_nh);
+		priv01.u16[0] = result.u16[0];
+		priv01.u16[4] = result.u16[2];
+		priv23.u16[0] = result.u16[4];
+		priv23.u16[4] = result.u16[6];
+
+		node_mbuf_priv1(mbuf0)->u = priv01.u64[0];
+		node_mbuf_priv1(mbuf1)->u = priv01.u64[1];
+		node_mbuf_priv1(mbuf2)->u = priv23.u64[0];
+		node_mbuf_priv1(mbuf3)->u = priv23.u64[1];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec = ((next_index == result.u16[1]) &&
+				       (result.u16[1] == result.u16[3]) &&
+				       (result.u16[3] == result.u16[5]) &&
+				       (result.u16[5] == result.u16[7]));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == result.u16[1]) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[1],
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == result.u16[3]) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[3],
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == result.u16[5]) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[5],
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == result.u16[7]) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, result.u16[7],
+						    from[3]);
+			}
+
+			from += 4;
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+		uint16_t next0;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		node_mbuf_priv1(mbuf0)->nh = (uint16_t)next_hop;
+		next_hop = next_hop >> 16;
+		next0 = (uint16_t)next_hop;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#endif /* __INCLUDE_IP4_LOOKUP_NEON_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 21/29] node: ipv4 lookup for x86
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (19 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 20/29] node: ipv4 lookup for arm64 jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 22/29] node: add ipv4 rewrite node jerinj
                           ` (9 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Pavan Nikhilesh <pbhagavatula@marvell.com>

Add IPv4 lookup process function for ip4_lookup
rte_node. This node performs LPM lookup using x86_64
vector supported RTE_LPM API on every packet received
and forwards it to a next node that is identified by
lookup result.

Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ip4_lookup.c     |   2 +
 lib/librte_node/ip4_lookup_sse.h | 244 +++++++++++++++++++++++++++++++
 2 files changed, 246 insertions(+)
 create mode 100644 lib/librte_node/ip4_lookup_sse.h

diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index 8ec4f0626..39c8d8db0 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -30,6 +30,8 @@ struct ip4_lookup_node_main {
 
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 #include "ip4_lookup_neon.h"
+#elif defined(RTE_ARCH_X86)
+#include "ip4_lookup_sse.h"
 #else
 
 static uint16_t
diff --git a/lib/librte_node/ip4_lookup_sse.h b/lib/librte_node/ip4_lookup_sse.h
new file mode 100644
index 000000000..a071cc591
--- /dev/null
+++ b/lib/librte_node/ip4_lookup_sse.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __INCLUDE_IP4_LOOKUP_SSE_H__
+#define __INCLUDE_IP4_LOOKUP_SSE_H__
+
+/* X86 SSE */
+static uint16_t
+ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	rte_edge_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ipv4_hdr;
+	uint32_t ip0, ip1, ip2, ip3;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	uint16_t n_left_from;
+	struct rte_lpm *lpm;
+	uint16_t held = 0;
+	uint32_t drop_nh;
+	rte_xmm_t dst;
+	__m128i dip; /* SSE register */
+	int rc, i;
+
+	/* Speculative next */
+	next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;
+	/* Drop node */
+	drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;
+
+	/* Get socket specific LPM from ctx */
+	lpm = *((struct rte_lpm **)node->ctx);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	if (n_left_from >= 4) {
+		for (i = 0; i < 4; i++)
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
+						sizeof(struct rte_ether_hdr)));
+	}
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	while (n_left_from >= 4) {
+		/* Prefetch next-next mbufs */
+		if (likely(n_left_from > 11)) {
+			rte_prefetch0(pkts[8]);
+			rte_prefetch0(pkts[9]);
+			rte_prefetch0(pkts[10]);
+			rte_prefetch0(pkts[11]);
+		}
+
+		/* Prefetch next mbuf data */
+		if (likely(n_left_from > 7)) {
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
+						sizeof(struct rte_ether_hdr)));
+			rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
+						sizeof(struct rte_ether_hdr)));
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip0 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf1 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf1, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip1 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		node_mbuf_priv1(mbuf1)->cksum = ipv4_hdr->hdr_checksum;
+		node_mbuf_priv1(mbuf1)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf2 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf2, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip2 = ipv4_hdr->dst_addr;
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		node_mbuf_priv1(mbuf2)->cksum = ipv4_hdr->hdr_checksum;
+		node_mbuf_priv1(mbuf2)->ttl = ipv4_hdr->time_to_live;
+
+		/* Extract DIP of mbuf3 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf3, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		ip3 = ipv4_hdr->dst_addr;
+
+		/* Prepare for lookup x4 */
+		dip = _mm_set_epi32(ip3, ip2, ip1, ip0);
+
+		/* Byte swap 4 IPV4 addresses. */
+		const __m128i bswap_mask = _mm_set_epi8(
+			12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
+		dip = _mm_shuffle_epi8(dip, bswap_mask);
+
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		node_mbuf_priv1(mbuf3)->cksum = ipv4_hdr->hdr_checksum;
+		node_mbuf_priv1(mbuf3)->ttl = ipv4_hdr->time_to_live;
+
+		/* Perform LPM lookup to get NH and next node */
+		rte_lpm_lookupx4(lpm, dip, dst.u32, drop_nh);
+
+		/* Extract next node id and NH */
+		node_mbuf_priv1(mbuf0)->nh = dst.u32[0] & 0xFFFF;
+		next0 = (dst.u32[0] >> 16);
+
+		node_mbuf_priv1(mbuf1)->nh = dst.u32[1] & 0xFFFF;
+		next1 = (dst.u32[1] >> 16);
+
+		node_mbuf_priv1(mbuf2)->nh = dst.u32[2] & 0xFFFF;
+		next2 = (dst.u32[2] >> 16);
+
+		node_mbuf_priv1(mbuf3)->nh = dst.u32[3] & 0xFFFF;
+		next3 = (dst.u32[3] >> 16);
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			(next_index ^ next0) | (next_index ^ next1) |
+			(next_index ^ next2) | (next_index ^ next3);
+
+		if (unlikely(fix_spec)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* Next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* Next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* Next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* Next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint32_t next_hop;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		/* Extract DIP of mbuf0 */
+		ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr *,
+						sizeof(struct rte_ether_hdr));
+		/* Extract cksum, ttl as ipv4 hdr is in cache */
+		node_mbuf_priv1(mbuf0)->cksum = ipv4_hdr->hdr_checksum;
+		node_mbuf_priv1(mbuf0)->ttl = ipv4_hdr->time_to_live;
+
+		rc = rte_lpm_lookup(lpm, rte_be_to_cpu_32(ipv4_hdr->dst_addr),
+				    &next_hop);
+		next_hop = (rc == 0) ? next_hop : drop_nh;
+
+		node_mbuf_priv1(mbuf0)->nh = next_hop & 0xFFFF;
+		next0 = (next_hop >> 16);
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	/* Copy things successfully speculated till now */
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+
+	return nb_objs;
+}
+
+#endif /* __INCLUDE_IP4_LOOKUP_SSE_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 22/29] node: add ipv4 rewrite node
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (20 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 21/29] node: ipv4 lookup for x86 jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
                           ` (8 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Kiran Kumar K <kirankumark@marvell.com>

Add ip4 rewrite process function for ip4_rewrite
rte_node. On every packet received by this node,
header is overwritten with new data before forwarding
it to next node. Header data to overwrite with is
identified by next hop id passed in mbuf priv data
by previous node.

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 lib/librte_node/Makefile           |   1 +
 lib/librte_node/ip4_rewrite.c      | 270 +++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h |  55 ++++++
 lib/librte_node/meson.build        |   2 +-
 4 files changed, 327 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/ip4_rewrite.c
 create mode 100644 lib/librte_node/ip4_rewrite_priv.h

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ad3f2e349..ebf473c66 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -22,6 +22,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_rx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
new file mode 100644
index 000000000..333e34761
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite.c
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_ip.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
+#include <rte_tcp.h>
+#include <rte_udp.h>
+#include <rte_vect.h>
+
+#include "rte_node_ip4_api.h"
+
+#include "ip4_rewrite_priv.h"
+#include "node_private.h"
+
+static struct ip4_rewrite_node_main *ip4_rewrite_nm;
+
+static uint16_t
+ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
+			 void **objs, uint16_t nb_objs)
+{
+	struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+	struct ip4_rewrite_nh_header *nh = ip4_rewrite_nm->nh;
+	uint16_t next0, next1, next2, next3, next_index;
+	struct rte_ipv4_hdr *ip0, *ip1, *ip2, *ip3;
+	uint16_t n_left_from, held = 0, last_spec = 0;
+	void *d0, *d1, *d2, *d3;
+	void **to_next, **from;
+	rte_xmm_t priv01;
+	rte_xmm_t priv23;
+	int i;
+
+	/* Speculative next as last next */
+	next_index = *(uint16_t *)node->ctx;
+	rte_prefetch0(nh);
+
+	pkts = (struct rte_mbuf **)objs;
+	from = objs;
+	n_left_from = nb_objs;
+
+	for (i = 0; i < 4 && i < n_left_from; i++)
+		rte_prefetch0(pkts[i]);
+
+	/* Get stream for the speculated next node */
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	/* Update Ethernet header of pkts */
+	while (n_left_from >= 4) {
+		if (likely(n_left_from > 7)) {
+			/* Prefetch only next-mbuf struct and priv area.
+			 * Data need not be prefetched as we only write.
+			 */
+			rte_prefetch0(pkts[4]);
+			rte_prefetch0(pkts[5]);
+			rte_prefetch0(pkts[6]);
+			rte_prefetch0(pkts[7]);
+		}
+
+		mbuf0 = pkts[0];
+		mbuf1 = pkts[1];
+		mbuf2 = pkts[2];
+		mbuf3 = pkts[3];
+
+		pkts += 4;
+		n_left_from -= 4;
+		priv01.u64[0] = node_mbuf_priv1(mbuf0)->u;
+		priv01.u64[1] = node_mbuf_priv1(mbuf1)->u;
+		priv23.u64[0] = node_mbuf_priv1(mbuf2)->u;
+		priv23.u64[1] = node_mbuf_priv1(mbuf3)->u;
+
+		/* Increment checksum by one. */
+		priv01.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv01.u32[3] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[1] += rte_cpu_to_be_16(0x0100);
+		priv23.u32[3] += rte_cpu_to_be_16(0x0100);
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf0 */
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[priv01.u16[0]].rewrite_data,
+			   nh[priv01.u16[0]].rewrite_len);
+
+		next0 = nh[priv01.u16[0]].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		ip0->time_to_live = priv01.u16[1] - 1;
+		ip0->hdr_checksum = priv01.u16[2] + priv01.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf1 */
+		d1 = rte_pktmbuf_mtod(mbuf1, void *);
+		rte_memcpy(d1, nh[priv01.u16[4]].rewrite_data,
+			   nh[priv01.u16[4]].rewrite_len);
+
+		next1 = nh[priv01.u16[4]].tx_node;
+		ip1 = (struct rte_ipv4_hdr *)((uint8_t *)d1 +
+					      sizeof(struct rte_ether_hdr));
+		ip1->time_to_live = priv01.u16[5] - 1;
+		ip1->hdr_checksum = priv01.u16[6] + priv01.u16[7];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf2 */
+		d2 = rte_pktmbuf_mtod(mbuf2, void *);
+		rte_memcpy(d2, nh[priv23.u16[0]].rewrite_data,
+			   nh[priv23.u16[0]].rewrite_len);
+		next2 = nh[priv23.u16[0]].tx_node;
+		ip2 = (struct rte_ipv4_hdr *)((uint8_t *)d2 +
+					      sizeof(struct rte_ether_hdr));
+		ip2->time_to_live = priv23.u16[1] - 1;
+		ip2->hdr_checksum = priv23.u16[2] + priv23.u16[3];
+
+		/* Update ttl,cksum rewrite ethernet hdr on mbuf3 */
+		d3 = rte_pktmbuf_mtod(mbuf3, void *);
+		rte_memcpy(d3, nh[priv23.u16[4]].rewrite_data,
+			   nh[priv23.u16[4]].rewrite_len);
+
+		next3 = nh[priv23.u16[4]].tx_node;
+		ip3 = (struct rte_ipv4_hdr *)((uint8_t *)d3 +
+					      sizeof(struct rte_ether_hdr));
+		ip3->time_to_live = priv23.u16[5] - 1;
+		ip3->hdr_checksum = priv23.u16[6] + priv23.u16[7];
+
+		/* Enqueue four to next node */
+		rte_edge_t fix_spec =
+			((next_index == next0) && (next0 == next1) &&
+			 (next1 == next2) && (next2 == next3));
+
+		if (unlikely(fix_spec == 0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			/* next0 */
+			if (next_index == next0) {
+				to_next[0] = from[0];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next0,
+						    from[0]);
+			}
+
+			/* next1 */
+			if (next_index == next1) {
+				to_next[0] = from[1];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next1,
+						    from[1]);
+			}
+
+			/* next2 */
+			if (next_index == next2) {
+				to_next[0] = from[2];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next2,
+						    from[2]);
+			}
+
+			/* next3 */
+			if (next_index == next3) {
+				to_next[0] = from[3];
+				to_next++;
+				held++;
+			} else {
+				rte_node_enqueue_x1(graph, node, next3,
+						    from[3]);
+			}
+
+			from += 4;
+
+			/* Change speculation if last two are same */
+			if ((next_index != next3) && (next2 == next3)) {
+				/* Put the current speculated node */
+				rte_node_next_stream_put(graph, node,
+							 next_index, held);
+				held = 0;
+
+				/* Get next speculated stream */
+				next_index = next3;
+				to_next = rte_node_next_stream_get(
+					graph, node, next_index, nb_objs);
+			}
+		} else {
+			last_spec += 4;
+		}
+	}
+
+	while (n_left_from > 0) {
+		uint16_t chksum;
+
+		mbuf0 = pkts[0];
+
+		pkts += 1;
+		n_left_from -= 1;
+
+		d0 = rte_pktmbuf_mtod(mbuf0, void *);
+		rte_memcpy(d0, nh[node_mbuf_priv1(mbuf0)->nh].rewrite_data,
+			   nh[node_mbuf_priv1(mbuf0)->nh].rewrite_len);
+
+		next0 = nh[node_mbuf_priv1(mbuf0)->nh].tx_node;
+		ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
+					      sizeof(struct rte_ether_hdr));
+		chksum = node_mbuf_priv1(mbuf0)->cksum +
+			 rte_cpu_to_be_16(0x0100);
+		chksum += chksum >= 0xffff;
+		ip0->hdr_checksum = chksum;
+		ip0->time_to_live = node_mbuf_priv1(mbuf0)->ttl - 1;
+
+		if (unlikely(next_index ^ next0)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next0, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+	/* Save the last next used */
+	*(uint16_t *)node->ctx = next_index;
+
+	return nb_objs;
+}
+
+static int
+ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+
+	RTE_SET_USED(graph);
+	RTE_SET_USED(node);
+	node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");
+
+	return 0;
+}
+
+static struct rte_node_register ip4_rewrite_node = {
+	.process = ip4_rewrite_node_process,
+	.name = "ip4_rewrite",
+	/* Default edge i.e '0' is pkt drop */
+	.nb_edges = 1,
+	.next_nodes = {
+		[0] = "pkt_drop",
+	},
+	.init = ip4_rewrite_node_init,
+};
+
+RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
new file mode 100644
index 000000000..420996a03
--- /dev/null
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#ifndef __INCLUDE_IP4_REWRITE_PRIV_H__
+#define __INCLUDE_IP4_REWRITE_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+
+#define RTE_GRAPH_IP4_REWRITE_MAX_NH 64
+#define RTE_GRAPH_IP4_REWRITE_MAX_LEN 56
+
+/**
+ * @internal
+ *
+ * Ipv4 rewrite next hop header data structure. Used to store port specific
+ * rewrite data.
+ */
+struct ip4_rewrite_nh_header {
+	uint16_t rewrite_len; /**< Header rewrite length. */
+	uint16_t tx_node;     /**< Tx node next index identifier. */
+	uint16_t enabled;     /**< NH enable flag */
+	uint16_t rsvd;
+	union {
+		struct {
+			struct rte_ether_addr dst;
+			/**< Destination mac address. */
+			struct rte_ether_addr src;
+			/**< Source mac address. */
+		};
+		uint8_t rewrite_data[RTE_GRAPH_IP4_REWRITE_MAX_LEN];
+		/**< Generic rewrite data */
+	};
+};
+
+/**
+ * @internal
+ *
+ * Ipv4 node main data structure.
+ */
+struct ip4_rewrite_node_main {
+	struct ip4_rewrite_nh_header nh[RTE_GRAPH_IP4_REWRITE_MAX_NH];
+	/**< Array of next hop header data */
+	uint16_t next_index[RTE_MAX_ETHPORTS];
+	/**< Next index of each configured port. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_IP4_REWRITE_PRIV_H__ */
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index 702d21a24..ed78791dd 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 23/29] node: add ipv4 rewrite and lookup ctrl API
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (21 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 22/29] node: add ipv4 rewrite node jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 24/29] node: add packet drop node jerinj
                           ` (7 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add ip4_rewrite and ip4_lookup ctrl API. ip4_lookup ctrl
API is used to add route entries for LPM lookup with
result data containing next hop id and next proto.
ip4_rewrite ctrl API is used to add rewrite data for
every next hop.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/ethdev_ctrl.c        | 18 ++++++-
 lib/librte_node/ip4_lookup.c         | 79 ++++++++++++++++++++++++++++
 lib/librte_node/ip4_rewrite.c        | 56 ++++++++++++++++++++
 lib/librte_node/ip4_rewrite_priv.h   | 22 ++++++++
 lib/librte_node/rte_node_ip4_api.h   | 38 +++++++++++++
 lib/librte_node/rte_node_version.map |  2 +
 6 files changed, 214 insertions(+), 1 deletion(-)

diff --git a/lib/librte_node/ethdev_ctrl.c b/lib/librte_node/ethdev_ctrl.c
index 599d20b0b..13b8b705f 100644
--- a/lib/librte_node/ethdev_ctrl.c
+++ b/lib/librte_node/ethdev_ctrl.c
@@ -11,6 +11,7 @@
 
 #include "ethdev_rx_priv.h"
 #include "ethdev_tx_priv.h"
+#include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
 static struct ethdev_ctrl {
@@ -21,14 +22,17 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 		    uint16_t nb_graphs)
 {
+	struct rte_node_register *ip4_rewrite_node;
 	struct ethdev_tx_node_main *tx_node_data;
 	uint16_t tx_q_used, rx_q_used, port_id;
 	struct rte_node_register *tx_node;
 	char name[RTE_NODE_NAMESIZE];
+	const char *next_nodes = name;
 	struct rte_mempool *mp;
+	int i, j, rc;
 	uint32_t id;
-	int i, j;
 
+	ip4_rewrite_node = ip4_rewrite_node_get();
 	tx_node_data = ethdev_tx_node_data_get();
 	tx_node = ethdev_tx_node_get();
 	for (i = 0; i < nb_confs; i++) {
@@ -92,6 +96,18 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
 
 		node_dbg("ethdev", "Tx node %s-%s: is at %u", tx_node->name,
 			 name, id);
+
+		/* Prepare the actual name of the cloned node */
+		snprintf(name, sizeof(name), "ethdev_tx-%u", port_id);
+
+		/* Add this tx port node as next to ip4_rewrite_node */
+		rte_node_edge_update(ip4_rewrite_node->id, RTE_EDGE_ID_INVALID,
+				     &next_nodes, 1);
+		/* Assuming edge id is the last one alloc'ed */
+		rc = ip4_rewrite_set_next(
+			port_id, rte_node_edge_count(ip4_rewrite_node->id) - 1);
+		if (rc < 0)
+			return rc;
 	}
 
 	ctrl.nb_graphs = nb_graphs;
diff --git a/lib/librte_node/ip4_lookup.c b/lib/librte_node/ip4_lookup.c
index 39c8d8db0..8e6379457 100644
--- a/lib/librte_node/ip4_lookup.c
+++ b/lib/librte_node/ip4_lookup.c
@@ -28,6 +28,8 @@ struct ip4_lookup_node_main {
 	struct rte_lpm *lpm_tbl[RTE_MAX_NUMA_NODES];
 };
 
+static struct ip4_lookup_node_main ip4_lookup_nm;
+
 #if defined(RTE_MACHINE_CPUFLAG_NEON)
 #include "ip4_lookup_neon.h"
 #elif defined(RTE_ARCH_X86)
@@ -109,12 +111,89 @@ ip4_lookup_node_process(struct rte_graph *graph, struct rte_node *node,
 
 #endif
 
+int
+rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+		       enum rte_node_ip4_lookup_next next_node)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	struct in_addr in;
+	uint8_t socket;
+	uint32_t val;
+	int ret;
+
+	in.s_addr = htonl(ip);
+	inet_ntop(AF_INET, &in, abuf, sizeof(abuf));
+	/* Embedded next node id into 24 bit next hop */
+	val = ((next_node << 16) | next_hop) & ((1ull << 24) - 1);
+	node_dbg("ip4_lookup", "LPM: Adding route %s / %d nh (0x%x)", abuf,
+		 depth, val);
+
+	for (socket = 0; socket < RTE_MAX_NUMA_NODES; socket++) {
+		if (!ip4_lookup_nm.lpm_tbl[socket])
+			continue;
+
+		ret = rte_lpm_add(ip4_lookup_nm.lpm_tbl[socket],
+				  ip, depth, val);
+		if (ret < 0) {
+			node_err("ip4_lookup",
+				 "Unable to add entry %s / %d nh (%x) to LPM table on sock %d, rc=%d\n",
+				 abuf, depth, val, socket, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int
+setup_lpm(struct ip4_lookup_node_main *nm, int socket)
+{
+	struct rte_lpm_config config_ipv4;
+	char s[RTE_LPM_NAMESIZE];
+
+	/* One LPM table per socket */
+	if (nm->lpm_tbl[socket])
+		return 0;
+
+	/* create the LPM table */
+	config_ipv4.max_rules = IPV4_L3FWD_LPM_MAX_RULES;
+	config_ipv4.number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S;
+	config_ipv4.flags = 0;
+	snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socket);
+	nm->lpm_tbl[socket] = rte_lpm_create(s, socket, &config_ipv4);
+	if (nm->lpm_tbl[socket] == NULL)
+		return -rte_errno;
+
+	return 0;
+}
+
 static int
 ip4_lookup_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
+	struct rte_lpm **lpm_p = (struct rte_lpm **)&node->ctx;
+	uint16_t socket, lcore_id;
+	static uint8_t init_once;
+	int rc;
+
 	RTE_SET_USED(graph);
 	RTE_SET_USED(node);
 
+	if (!init_once) {
+		/* Setup LPM tables for all sockets */
+		RTE_LCORE_FOREACH(lcore_id)
+		{
+			socket = rte_lcore_to_socket_id(lcore_id);
+			rc = setup_lpm(&ip4_lookup_nm, socket);
+			if (rc) {
+				node_err("ip4_lookup",
+					 "Failed to setup lpm tbl for sock %u, rc=%d",
+					 socket, rc);
+				return rc;
+			}
+		}
+		init_once = 1;
+	}
+	*lpm_p = ip4_lookup_nm.lpm_tbl[graph->socket];
 	node_dbg("ip4_lookup", "Initialized ip4_lookup node");
 
 	return 0;
diff --git a/lib/librte_node/ip4_rewrite.c b/lib/librte_node/ip4_rewrite.c
index 333e34761..bb7f671b5 100644
--- a/lib/librte_node/ip4_rewrite.c
+++ b/lib/librte_node/ip4_rewrite.c
@@ -256,6 +256,56 @@ ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 	return 0;
 }
 
+int
+ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index)
+{
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+	ip4_rewrite_nm->next_index[port_id] = next_index;
+
+	return 0;
+}
+
+int
+rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			 uint8_t rewrite_len, uint16_t dst_port)
+{
+	struct ip4_rewrite_nh_header *nh;
+
+	if (next_hop >= RTE_GRAPH_IP4_REWRITE_MAX_NH)
+		return -EINVAL;
+
+	if (rewrite_len > RTE_GRAPH_IP4_REWRITE_MAX_LEN)
+		return -EINVAL;
+
+	if (ip4_rewrite_nm == NULL) {
+		ip4_rewrite_nm = rte_zmalloc(
+			"ip4_rewrite", sizeof(struct ip4_rewrite_node_main),
+			RTE_CACHE_LINE_SIZE);
+		if (ip4_rewrite_nm == NULL)
+			return -ENOMEM;
+	}
+
+	/* Check if dst port doesn't exist as edge */
+	if (!ip4_rewrite_nm->next_index[dst_port])
+		return -EINVAL;
+
+	/* Update next hop */
+	nh = &ip4_rewrite_nm->nh[next_hop];
+
+	memcpy(nh->rewrite_data, rewrite_data, rewrite_len);
+	nh->tx_node = ip4_rewrite_nm->next_index[dst_port];
+	nh->rewrite_len = rewrite_len;
+	nh->enabled = true;
+
+	return 0;
+}
+
 static struct rte_node_register ip4_rewrite_node = {
 	.process = ip4_rewrite_node_process,
 	.name = "ip4_rewrite",
@@ -267,4 +317,10 @@ static struct rte_node_register ip4_rewrite_node = {
 	.init = ip4_rewrite_node_init,
 };
 
+struct rte_node_register *
+ip4_rewrite_node_get(void)
+{
+	return &ip4_rewrite_node;
+}
+
 RTE_NODE_REGISTER(ip4_rewrite_node);
diff --git a/lib/librte_node/ip4_rewrite_priv.h b/lib/librte_node/ip4_rewrite_priv.h
index 420996a03..80f0abdc9 100644
--- a/lib/librte_node/ip4_rewrite_priv.h
+++ b/lib/librte_node/ip4_rewrite_priv.h
@@ -48,6 +48,28 @@ struct ip4_rewrite_node_main {
 	/**< Next index of each configured port. */
 };
 
+/**
+ * @internal
+ *
+ * Get the ipv4 rewrite node.
+ *
+ * @retrun
+ *   Pointer to the ipv4 rewrite node.
+ */
+struct rte_node_register *ip4_rewrite_node_get(void);
+
+/**
+ * @internal
+ *
+ * Set the Edge index of a given port_id.
+ *
+ * @param port_id
+ *   Ethernet port identifier.
+ * @param next_index
+ *   Edge index of the Given Tx node.
+ */
+int ip4_rewrite_set_next(uint16_t port_id, uint16_t next_index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_ip4_api.h b/lib/librte_node/rte_node_ip4_api.h
index 0b9ba48a2..31a752b00 100644
--- a/lib/librte_node/rte_node_ip4_api.h
+++ b/lib/librte_node/rte_node_ip4_api.h
@@ -33,6 +33,44 @@ enum rte_node_ip4_lookup_next {
 	/**< Number of next nodes of lookup node. */
 };
 
+/**
+ * Add ipv4 route to lookup table.
+ *
+ * @param ip
+ *   IP address of route to be added.
+ * @param depth
+ *   Depth of the rule to be added.
+ * @param next_hop
+ *   Next hop id of the rule result to be added.
+ * @param next_node
+ *   Next node to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_route_add(uint32_t ip, uint8_t depth, uint16_t next_hop,
+			   enum rte_node_ip4_lookup_next next_node);
+
+/**
+ * Add a next hop's rewrite data.
+ *
+ * @param next_hop
+ *   Next hop id to add rewrite data to.
+ * @param rewrite_data
+ *   Rewrite data.
+ * @param rewrite_len
+ *   Length of rewrite data.
+ * @param dst_port
+ *   Destination port to redirect traffic to.
+ *
+ * @return
+ *   0 on success, negative otherwise.
+ */
+__rte_experimental
+int rte_node_ip4_rewrite_add(uint16_t next_hop, uint8_t *rewrite_data,
+			     uint8_t rewrite_len, uint16_t dst_port);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_node/rte_node_version.map b/lib/librte_node/rte_node_version.map
index c6c71bd02..a799b0d38 100644
--- a/lib/librte_node/rte_node_version.map
+++ b/lib/librte_node/rte_node_version.map
@@ -2,6 +2,8 @@ EXPERIMENTAL {
 	global:
 
 	rte_node_eth_config;
+	rte_node_ip4_route_add;
+	rte_node_ip4_rewrite_add;
 	rte_node_logtype;
 	local: *;
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev]  [PATCH v5 24/29] node: add packet drop node
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (22 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
                           ` (6 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Nithin Dabilpuram, Pavan Nikhilesh
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add packet drop node process function for pkt_drop
rte_node. This node simply free's every object received as
an rte_mbuf to its rte_pktmbuf pool.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
---
 lib/librte_node/Makefile    |  1 +
 lib/librte_node/meson.build |  2 +-
 lib/librte_node/pkt_drop.c  | 26 ++++++++++++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_node/pkt_drop.c

diff --git a/lib/librte_node/Makefile b/lib/librte_node/Makefile
index ebf473c66..322b651c8 100644
--- a/lib/librte_node/Makefile
+++ b/lib/librte_node/Makefile
@@ -23,6 +23,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_tx.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ethdev_ctrl.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_lookup.c
 SRCS-$(CONFIG_RTE_LIBRTE_NODE) += ip4_rewrite.c
+SRCS-$(CONFIG_RTE_LIBRTE_NODE) += pkt_drop.c
 
 # install header files
 SYMLINK-$(CONFIG_RTE_LIBRTE_NODE)-include += rte_node_ip4_api.h
diff --git a/lib/librte_node/meson.build b/lib/librte_node/meson.build
index ed78791dd..8aa93654b 100644
--- a/lib/librte_node/meson.build
+++ b/lib/librte_node/meson.build
@@ -2,7 +2,7 @@
 # Copyright(C) 2020 Marvell International Ltd.
 
 sources = files('null.c', 'log.c', 'ethdev_rx.c', 'ethdev_tx.c', 'ip4_lookup.c',
-		'ip4_rewrite.c', 'ethdev_ctrl.c')
+		'ip4_rewrite.c', 'pkt_drop.c', 'ethdev_ctrl.c')
 headers = files('rte_node_ip4_api.h', 'rte_node_eth_api.h')
 allow_experimental_apis = true
 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/librte_node/pkt_drop.c b/lib/librte_node/pkt_drop.c
new file mode 100644
index 000000000..c35001323
--- /dev/null
+++ b/lib/librte_node/pkt_drop.c
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <rte_debug.h>
+#include <rte_graph.h>
+#include <rte_mbuf.h>
+
+static uint16_t
+pkt_drop_process(struct rte_graph *graph, struct rte_node *node, void **objs,
+		 uint16_t nb_objs)
+{
+	RTE_SET_USED(node);
+	RTE_SET_USED(graph);
+
+	rte_pktmbuf_free_bulk((struct rte_mbuf **)objs, nb_objs);
+
+	return nb_objs;
+}
+
+static struct rte_node_register pkt_drop_node = {
+	.process = pkt_drop_process,
+	.name = "pkt_drop",
+};
+
+RTE_NODE_REGISTER(pkt_drop_node);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 25/29] l3fwd-graph: add graph based l3fwd skeleton
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (23 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 24/29] node: add packet drop node jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 26/29] l3fwd-graph: add ethdev configuration changes jerinj
                           ` (5 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Thomas Monjalon, Marko Kovacevic, Ori Kam, Bruce Richardson,
	Radu Nicolau, Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori,
	Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph based l3fwd application skeleton with cmdline
parsing support inline with normal l3fwd.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                      |   3 +
 examples/Makefile                |   3 +
 examples/l3fwd-graph/Makefile    |  58 ++++
 examples/l3fwd-graph/main.c      | 534 +++++++++++++++++++++++++++++++
 examples/l3fwd-graph/meson.build |  13 +
 examples/meson.build             |   6 +-
 6 files changed, 615 insertions(+), 2 deletions(-)
 create mode 100644 examples/l3fwd-graph/Makefile
 create mode 100644 examples/l3fwd-graph/main.c
 create mode 100644 examples/l3fwd-graph/meson.build

diff --git a/MAINTAINERS b/MAINTAINERS
index 55fb9bbb0..de57f452b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1574,6 +1574,9 @@ F: doc/guides/sample_app_ug/l2_forward_event.rst
 F: examples/l3fwd/
 F: doc/guides/sample_app_ug/l3_forward.rst
 
+M: Nithin Dabilpuram <ndabilpuram@marvell.com>
+F: examples/l3fwd-graph/
+
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
 
diff --git a/examples/Makefile b/examples/Makefile
index feff79784..b7e99a2f7 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -51,6 +51,9 @@ DIRS-$(CONFIG_RTE_LIBRTE_ACL) += l3fwd-acl
 ifeq ($(CONFIG_RTE_LIBRTE_LPM)$(CONFIG_RTE_LIBRTE_HASH),yy)
 DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH),y)
+DIRS-y += l3fwd-graph
+endif
 DIRS-y += link_status_interrupt
 DIRS-y += multi_process
 DIRS-y += ntb
diff --git a/examples/l3fwd-graph/Makefile b/examples/l3fwd-graph/Makefile
new file mode 100644
index 000000000..f3cf275ec
--- /dev/null
+++ b/examples/l3fwd-graph/Makefile
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# binary name
+APP = l3fwd-graph
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+# Build using pkg-config variables if possible
+ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)
+
+all: shared
+.PHONY: shared static
+shared: build/$(APP)-shared
+	ln -sf $(APP)-shared build/$(APP)
+static: build/$(APP)-static
+	ln -sf $(APP)-static build/$(APP)
+
+PKGCONF=pkg-config --define-prefix
+
+PC_FILE := $(shell $(PKGCONF) --path libdpdk)
+CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) -DALLOW_EXPERIMENTAL_API
+LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
+LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)
+
+build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
+
+build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
+	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
+
+build:
+	@mkdir -p $@
+
+.PHONY: clean
+clean:
+	rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
+	test -d build && rmdir -p build || true
+
+else # Build using legacy build system
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, detect a build directory, by looking for a path with a .config
+RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+CFLAGS += -I$(SRCDIR)
+CFLAGS += -O3 $(USER_FLAGS)
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+endif
diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
new file mode 100644
index 000000000..ab6713488
--- /dev/null
+++ b/examples/l3fwd-graph/main.c
@@ -0,0 +1,534 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_branch_prediction.h>
+#include <rte_common.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_per_lcore.h>
+#include <rte_string_fns.h>
+#include <rte_vect.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
+
+/* Log type */
+#define RTE_LOGTYPE_L3FWD_GRAPH RTE_LOGTYPE_USER1
+
+/*
+ * Configurable number of RX/TX ring descriptors
+ */
+#define RTE_TEST_RX_DESC_DEFAULT 1024
+#define RTE_TEST_TX_DESC_DEFAULT 1024
+
+#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
+#define MAX_RX_QUEUE_PER_PORT 128
+
+#define MAX_RX_QUEUE_PER_LCORE 16
+
+#define MAX_LCORE_PARAMS 1024
+
+#define NB_SOCKETS 8
+
+/**< Ports set in promiscuous mode off by default. */
+static int promiscuous_on;
+
+static int numa_on = 1;	  /**< NUMA is enabled by default. */
+static int per_port_pool; /**< Use separate buffer pools per port; disabled */
+			  /**< by default */
+
+static volatile bool force_quit;
+
+/* Ethernet addresses of ports */
+static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+xmm_t val_eth[RTE_MAX_ETHPORTS];
+
+/* Mask of enabled ports */
+static uint32_t enabled_port_mask;
+
+struct lcore_rx_queue {
+	uint16_t port_id;
+	uint8_t queue_id;
+};
+
+/* Lcore conf */
+struct lcore_conf {
+	uint16_t n_rx_queue;
+	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+} __rte_cache_aligned;
+
+static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
+
+struct lcore_params {
+	uint16_t port_id;
+	uint8_t queue_id;
+	uint8_t lcore_id;
+} __rte_cache_aligned;
+
+static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
+static struct lcore_params lcore_params_array_default[] = {
+	{0, 0, 2}, {0, 1, 2}, {0, 2, 2}, {1, 0, 2}, {1, 1, 2},
+	{1, 2, 2}, {2, 0, 2}, {3, 0, 3}, {3, 1, 3},
+};
+
+static struct lcore_params *lcore_params = lcore_params_array_default;
+static uint16_t nb_lcore_params = RTE_DIM(lcore_params_array_default);
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_RSS,
+		.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+	},
+	.rx_adv_conf = {
+		.rss_conf = {
+				.rss_key = NULL,
+				.rss_hf = ETH_RSS_IP,
+		},
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+};
+
+static int
+check_lcore_params(void)
+{
+	uint8_t queue, lcore;
+	int socketid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		queue = lcore_params[i].queue_id;
+		if (queue >= MAX_RX_QUEUE_PER_PORT) {
+			printf("Invalid queue number: %hhu\n", queue);
+			return -1;
+		}
+		lcore = lcore_params[i].lcore_id;
+		if (!rte_lcore_is_enabled(lcore)) {
+			printf("Error: lcore %hhu is not enabled in lcore mask\n",
+			       lcore);
+			return -1;
+		}
+
+		if (lcore == rte_get_master_lcore()) {
+			printf("Error: lcore %u is master lcore\n", lcore);
+			return -1;
+		}
+		socketid = rte_lcore_to_socket_id(lcore);
+		if ((socketid != 0) && (numa_on == 0)) {
+			printf("Warning: lcore %hhu is on socket %d with numa off\n",
+			       lcore, socketid);
+		}
+	}
+
+	return 0;
+}
+
+static int
+check_port_config(void)
+{
+	uint16_t portid;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		portid = lcore_params[i].port_id;
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("Port %u is not enabled in port mask\n", portid);
+			return -1;
+		}
+		if (!rte_eth_dev_is_valid_port(portid)) {
+			printf("Port %u is not present on the board\n", portid);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+init_lcore_rx_queues(void)
+{
+	uint16_t i, nb_rx_queue;
+	uint8_t lcore;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		lcore = lcore_params[i].lcore_id;
+		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
+		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
+			printf("Error: too many queues (%u) for lcore: %u\n",
+			       (unsigned int)nb_rx_queue + 1,
+			       (unsigned int)lcore);
+			return -1;
+		}
+
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
+			lcore_params[i].port_id;
+		lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
+			lcore_params[i].queue_id;
+		lcore_conf[lcore].n_rx_queue++;
+	}
+
+	return 0;
+}
+
+/* Display usage */
+static void
+print_usage(const char *prgname)
+{
+	fprintf(stderr,
+		"%s [EAL options] --"
+		" -p PORTMASK"
+		" [-P]"
+		" --config (port,queue,lcore)[,(port,queue,lcore)]"
+		" [--eth-dest=X,MM:MM:MM:MM:MM:MM]"
+		" [--enable-jumbo [--max-pkt-len PKTLEN]]"
+		" [--no-numa]"
+		" [--per-port-pool]\n\n"
+
+		"  -p PORTMASK: Hexadecimal bitmask of ports to configure\n"
+		"  -P : Enable promiscuous mode\n"
+		"  --config (port,queue,lcore): Rx queue configuration\n"
+		"  --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for "
+		"port X\n"
+		"  --enable-jumbo: Enable jumbo frames\n"
+		"  --max-pkt-len: Under the premise of enabling jumbo,\n"
+		"                 maximum packet length in decimal (64-9600)\n"
+		"  --no-numa: Disable numa awareness\n"
+		"  --per-port-pool: Use separate buffer pool per port\n\n",
+		prgname);
+}
+
+static int
+parse_max_pkt_len(const char *pktlen)
+{
+	unsigned long len;
+	char *end = NULL;
+
+	/* Parse decimal string */
+	len = strtoul(pktlen, &end, 10);
+	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (len == 0)
+		return -1;
+
+	return len;
+}
+
+static int
+parse_portmask(const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse hexadecimal string */
+	pm = strtoul(portmask, &end, 16);
+	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (pm == 0)
+		return -1;
+
+	return pm;
+}
+
+static int
+parse_config(const char *q_arg)
+{
+	enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD };
+	unsigned long int_fld[_NUM_FLD];
+	const char *p, *p0 = q_arg;
+	char *str_fld[_NUM_FLD];
+	uint32_t size;
+	char s[256];
+	char *end;
+	int i;
+
+	nb_lcore_params = 0;
+
+	while ((p = strchr(p0, '(')) != NULL) {
+		++p;
+		p0 = strchr(p, ')');
+		if (p0 == NULL)
+			return -1;
+
+		size = p0 - p;
+		if (size >= sizeof(s))
+			return -1;
+
+		memcpy(s, p, size);
+		s[size] = '\0';
+		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
+		    _NUM_FLD)
+			return -1;
+		for (i = 0; i < _NUM_FLD; i++) {
+			errno = 0;
+			int_fld[i] = strtoul(str_fld[i], &end, 0);
+			if (errno != 0 || end == str_fld[i])
+				return -1;
+		}
+
+		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
+			printf("Exceeded max number of lcore params: %hu\n",
+			       nb_lcore_params);
+			return -1;
+		}
+
+		if (int_fld[FLD_PORT] >= RTE_MAX_ETHPORTS ||
+		    int_fld[FLD_LCORE] >= RTE_MAX_LCORE) {
+			printf("Invalid port/lcore id\n");
+			return -1;
+		}
+
+		lcore_params_array[nb_lcore_params].port_id =
+			(uint8_t)int_fld[FLD_PORT];
+		lcore_params_array[nb_lcore_params].queue_id =
+			(uint8_t)int_fld[FLD_QUEUE];
+		lcore_params_array[nb_lcore_params].lcore_id =
+			(uint8_t)int_fld[FLD_LCORE];
+		++nb_lcore_params;
+	}
+	lcore_params = lcore_params_array;
+
+	return 0;
+}
+
+static void
+parse_eth_dest(const char *optarg)
+{
+	uint8_t c, *dest, peer_addr[6];
+	uint16_t portid;
+	char *port_end;
+
+	errno = 0;
+	portid = strtoul(optarg, &port_end, 10);
+	if (errno != 0 || port_end == optarg || *port_end++ != ',')
+		rte_exit(EXIT_FAILURE, "Invalid eth-dest: %s", optarg);
+	if (portid >= RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE,
+			 "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", portid,
+			 RTE_MAX_ETHPORTS);
+
+	if (cmdline_parse_etheraddr(NULL, port_end, &peer_addr,
+				    sizeof(peer_addr)) < 0)
+		rte_exit(EXIT_FAILURE, "Invalid ethernet address: %s\n",
+			 port_end);
+	dest = (uint8_t *)&dest_eth_addr[portid];
+	for (c = 0; c < 6; c++)
+		dest[c] = peer_addr[c];
+	*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+}
+
+#define MAX_JUMBO_PKT_LEN  9600
+#define MEMPOOL_CACHE_SIZE 256
+
+static const char short_options[] = "p:" /* portmask */
+				    "P"	 /* promiscuous */
+	;
+
+#define CMD_LINE_OPT_CONFIG	   "config"
+#define CMD_LINE_OPT_ETH_DEST	   "eth-dest"
+#define CMD_LINE_OPT_NO_NUMA	   "no-numa"
+#define CMD_LINE_OPT_ENABLE_JUMBO  "enable-jumbo"
+#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool"
+enum {
+	/* Long options mapped to a short option */
+
+	/* First long only option value must be >= 256, so that we won't
+	 * conflict with short options
+	 */
+	CMD_LINE_OPT_MIN_NUM = 256,
+	CMD_LINE_OPT_CONFIG_NUM,
+	CMD_LINE_OPT_ETH_DEST_NUM,
+	CMD_LINE_OPT_NO_NUMA_NUM,
+	CMD_LINE_OPT_ENABLE_JUMBO_NUM,
+	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
+};
+
+static const struct option lgopts[] = {
+	{CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM},
+	{CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM},
+	{CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM},
+	{CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM},
+	{CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL},
+	{NULL, 0, 0, 0},
+};
+
+/*
+ * This expression is used to calculate the number of mbufs needed
+ * depending on user input, taking  into account memory for rx and
+ * tx hardware rings, cache per lcore and mtable per port per lcore.
+ * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum
+ * value of 8192
+ */
+#define NB_MBUF(nports)                                                        \
+	RTE_MAX((nports * nb_rx_queue * nb_rxd +                               \
+		 nports * nb_lcores * RTE_GRAPH_BURST_SIZE +                   \
+		 nports * n_tx_queue * nb_txd +                                \
+		 nb_lcores * MEMPOOL_CACHE_SIZE), 8192u)
+
+/* Parse the argument given in the command line of the application */
+static int
+parse_args(int argc, char **argv)
+{
+	char *prgname = argv[0];
+	int option_index;
+	char **argvopt;
+	int opt, ret;
+
+	argvopt = argv;
+
+	/* Error or normal output strings. */
+	while ((opt = getopt_long(argc, argvopt, short_options, lgopts,
+				  &option_index)) != EOF) {
+
+		switch (opt) {
+		/* Portmask */
+		case 'p':
+			enabled_port_mask = parse_portmask(optarg);
+			if (enabled_port_mask == 0) {
+				fprintf(stderr, "Invalid portmask\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case 'P':
+			promiscuous_on = 1;
+			break;
+
+		/* Long options */
+		case CMD_LINE_OPT_CONFIG_NUM:
+			ret = parse_config(optarg);
+			if (ret) {
+				fprintf(stderr, "Invalid config\n");
+				print_usage(prgname);
+				return -1;
+			}
+			break;
+
+		case CMD_LINE_OPT_ETH_DEST_NUM:
+			parse_eth_dest(optarg);
+			break;
+
+		case CMD_LINE_OPT_NO_NUMA_NUM:
+			numa_on = 0;
+			break;
+
+		case CMD_LINE_OPT_ENABLE_JUMBO_NUM: {
+			const struct option lenopts = {"max-pkt-len",
+						       required_argument, 0, 0};
+
+			port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME;
+			port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS;
+
+			/*
+			 * if no max-pkt-len set, use the default
+			 * value RTE_ETHER_MAX_LEN.
+			 */
+			if (getopt_long(argc, argvopt, "", &lenopts,
+					&option_index) == 0) {
+				ret = parse_max_pkt_len(optarg);
+				if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) {
+					fprintf(stderr, "Invalid maximum "
+							"packet length\n");
+					print_usage(prgname);
+					return -1;
+				}
+				port_conf.rxmode.max_rx_pkt_len = ret;
+			}
+			break;
+		}
+
+		case CMD_LINE_OPT_PARSE_PER_PORT_POOL:
+			printf("Per port buffer pool is enabled\n");
+			per_port_pool = 1;
+			break;
+
+		default:
+			print_usage(prgname);
+			return -1;
+		}
+	}
+
+	if (optind >= 0)
+		argv[optind - 1] = prgname;
+	ret = optind - 1;
+	optind = 1; /* Reset getopt lib */
+
+	return ret;
+}
+
+static void
+signal_handler(int signum)
+{
+	if (signum == SIGINT || signum == SIGTERM) {
+		printf("\n\nSignal %d received, preparing to exit...\n",
+		       signum);
+		force_quit = true;
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	uint16_t portid;
+	int ret;
+
+	/* Init EAL */
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+	argc -= ret;
+	argv += ret;
+
+	force_quit = false;
+	signal(SIGINT, signal_handler);
+	signal(SIGTERM, signal_handler);
+
+	/* Pre-init dst MACs for all ports to 02:00:00:00:00:xx */
+	for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
+		dest_eth_addr[portid] =
+			RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40);
+		*(uint64_t *)(val_eth + portid) = dest_eth_addr[portid];
+	}
+
+	/* Parse application arguments (after the EAL ones) */
+	ret = parse_args(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid L3FWD_GRAPH parameters\n");
+
+	if (check_lcore_params() < 0)
+		rte_exit(EXIT_FAILURE, "check_lcore_params() failed\n");
+
+	ret = init_lcore_rx_queues();
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues() failed\n");
+
+	if (check_port_config() < 0)
+		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
+
+	printf("Bye...\n");
+
+	return ret;
+}
diff --git a/examples/l3fwd-graph/meson.build b/examples/l3fwd-graph/meson.build
new file mode 100644
index 000000000..a816bd890
--- /dev/null
+++ b/examples/l3fwd-graph/meson.build
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2020 Marvell International Ltd.
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+deps += ['graph', 'eal', 'lpm', 'ethdev', 'node' ]
+sources = files(
+	'main.c'
+)
+allow_experimental_apis = true
diff --git a/examples/meson.build b/examples/meson.build
index 1f2b6f516..3b540012f 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -2,8 +2,10 @@
 # Copyright(c) 2017-2019 Intel Corporation
 
 driver_libs = []
+node_libs = []
 if get_option('default_library') == 'static'
 	driver_libs = dpdk_drivers
+	node_libs = dpdk_graph_nodes
 endif
 
 execinfo = cc.find_library('execinfo', required: false)
@@ -23,7 +25,7 @@ all_examples = [
 	'l2fwd', 'l2fwd-cat', 'l2fwd-event',
 	'l2fwd-crypto', 'l2fwd-jobstats',
 	'l2fwd-keepalive', 'l3fwd',
-	'l3fwd-acl', 'l3fwd-power',
+	'l3fwd-acl', 'l3fwd-power', 'l3fwd-graph',
 	'link_status_interrupt',
 	'multi_process/client_server_mp/mp_client',
 	'multi_process/client_server_mp/mp_server',
@@ -99,7 +101,7 @@ foreach example: examples
 		endif
 		executable('dpdk-' + name, sources,
 			include_directories: includes,
-			link_whole: driver_libs,
+			link_whole: driver_libs + node_libs,
 			link_args: dpdk_extra_ldflags,
 			c_args: cflags,
 			dependencies: dep_objs)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 26/29] l3fwd-graph: add ethdev configuration changes
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (24 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 27/29] l3fwd-graph: add graph config and main loop jerinj
                           ` (4 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add changes to ethdev port and queue configuration based
on command line parameters for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 350 +++++++++++++++++++++++++++++++++++-
 1 file changed, 349 insertions(+), 1 deletion(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index ab6713488..7ea788589 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -20,8 +20,10 @@
 
 #include <rte_branch_prediction.h>
 #include <rte_common.h>
+#include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
 #include <rte_per_lcore.h>
@@ -49,6 +51,10 @@
 
 #define NB_SOCKETS 8
 
+/* Static global variables used within this file. */
+static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
+static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
+
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
@@ -60,6 +66,7 @@ static volatile bool force_quit;
 
 /* Ethernet addresses of ports */
 static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS];
+static struct rte_ether_addr ports_eth_addr[RTE_MAX_ETHPORTS];
 xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 /* Mask of enabled ports */
@@ -110,6 +117,8 @@ static struct rte_eth_conf port_conf = {
 	},
 };
 
+static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
+
 static int
 check_lcore_params(void)
 {
@@ -165,6 +174,27 @@ check_port_config(void)
 	return 0;
 }
 
+static uint8_t
+get_port_n_rx_queues(const uint16_t port)
+{
+	int queue = -1;
+	uint16_t i;
+
+	for (i = 0; i < nb_lcore_params; ++i) {
+		if (lcore_params[i].port_id == port) {
+			if (lcore_params[i].queue_id == queue + 1)
+				queue = lcore_params[i].queue_id;
+			else
+				rte_exit(EXIT_FAILURE,
+					 "Queue ids of the port %d must be"
+					 " in sequence and must start with 0\n",
+					 lcore_params[i].port_id);
+		}
+	}
+
+	return (uint8_t)(++queue);
+}
+
 static int
 init_lcore_rx_queues(void)
 {
@@ -479,6 +509,120 @@ parse_args(int argc, char **argv)
 	return ret;
 }
 
+static void
+print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
+{
+	char buf[RTE_ETHER_ADDR_FMT_SIZE];
+	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
+	printf("%s%s", name, buf);
+}
+
+static int
+init_mem(uint16_t portid, uint32_t nb_mbuf)
+{
+	uint32_t lcore_id;
+	int socketid;
+	char s[64];
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		if (numa_on)
+			socketid = rte_lcore_to_socket_id(lcore_id);
+		else
+			socketid = 0;
+
+		if (socketid >= NB_SOCKETS) {
+			rte_exit(EXIT_FAILURE,
+				 "Socket %d of lcore %u is out of range %d\n",
+				 socketid, lcore_id, NB_SOCKETS);
+		}
+
+		if (pktmbuf_pool[portid][socketid] == NULL) {
+			snprintf(s, sizeof(s), "mbuf_pool_%d:%d", portid,
+				 socketid);
+			/* Create a pool with priv size of a cacheline */
+			pktmbuf_pool[portid][socketid] =
+				rte_pktmbuf_pool_create(
+					s, nb_mbuf, MEMPOOL_CACHE_SIZE,
+					RTE_CACHE_LINE_SIZE,
+					RTE_MBUF_DEFAULT_BUF_SIZE, socketid);
+			if (pktmbuf_pool[portid][socketid] == NULL)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot init mbuf pool on socket %d\n",
+					 socketid);
+			else
+				printf("Allocated mbuf pool on socket %d\n",
+				       socketid);
+		}
+	}
+
+	return 0;
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90  /* 9s (90 * 100ms) in total */
+	uint8_t count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+	uint16_t portid;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		if (force_quit)
+			return;
+		all_ports_up = 1;
+		RTE_ETH_FOREACH_DEV(portid)
+		{
+			if (force_quit)
+				return;
+			if ((port_mask & (1 << portid)) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(portid, &link);
+			/* Print link status if flag set */
+			if (print_flag == 1) {
+				if (link.link_status)
+					printf("Port%d Link Up. Speed %u Mbps "
+					       "-%s\n",
+					       portid, link.link_speed,
+					       (link.link_duplex ==
+						ETH_LINK_FULL_DUPLEX)
+						       ? ("full-duplex")
+						       : ("half-duplex\n"));
+				else
+					printf("Port %d Link Down\n", portid);
+				continue;
+			}
+			/* Clear all_ports_up flag if any link down */
+			if (link.link_status == ETH_LINK_DOWN) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+		/* After finally printing all link status, get out */
+		if (print_flag == 1)
+			break;
+
+		if (all_ports_up == 0) {
+			printf(".");
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		/* Set the print_flag if all ports up or timeout */
+		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+			print_flag = 1;
+			printf("Done\n");
+		}
+	}
+}
+
 static void
 signal_handler(int signum)
 {
@@ -492,7 +636,14 @@ signal_handler(int signum)
 int
 main(int argc, char **argv)
 {
-	uint16_t portid;
+	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_eth_dev_info dev_info;
+	uint32_t n_tx_queue, nb_lcores;
+	struct rte_eth_txconf *txconf;
+	uint16_t queueid, portid;
+	struct lcore_conf *qconf;
+	uint32_t lcore_id;
+	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -528,6 +679,203 @@ main(int argc, char **argv)
 	if (check_port_config() < 0)
 		rte_exit(EXIT_FAILURE, "check_port_config() failed\n");
 
+	nb_ports = rte_eth_dev_count_avail();
+	nb_lcores = rte_lcore_count();
+
+	/* Initialize all ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		struct rte_eth_conf local_port_conf = port_conf;
+
+		/* Skip ports that are not enabled */
+		if ((enabled_port_mask & (1 << portid)) == 0) {
+			printf("\nSkipping disabled port %d\n", portid);
+			continue;
+		}
+
+		/* Init port */
+		printf("Initializing port %d ... ", portid);
+		fflush(stdout);
+
+		nb_rx_queue = get_port_n_rx_queues(portid);
+		n_tx_queue = nb_lcores;
+		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
+			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
+		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
+		       nb_rx_queue, n_tx_queue);
+
+		rte_eth_dev_info_get(portid, &dev_info);
+		if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+			local_port_conf.txmode.offloads |=
+				DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
+			dev_info.flow_type_rss_offloads;
+		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
+		    port_conf.rx_adv_conf.rss_conf.rss_hf) {
+			printf("Port %u modified RSS hash function based on "
+			       "hardware support,"
+			       "requested:%#" PRIx64 " configured:%#" PRIx64
+			       "\n",
+			       portid, port_conf.rx_adv_conf.rss_conf.rss_hf,
+			       local_port_conf.rx_adv_conf.rss_conf.rss_hf);
+		}
+
+		ret = rte_eth_dev_configure(portid, nb_rx_queue,
+					    n_tx_queue, &local_port_conf);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot configure device: err=%d, port=%d\n",
+				 ret, portid);
+
+		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
+						       &nb_txd);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Cannot adjust number of descriptors: err=%d, "
+				 "port=%d\n",
+				 ret, portid);
+
+		rte_eth_macaddr_get(portid, &ports_eth_addr[portid]);
+		print_ethaddr(" Address:", &ports_eth_addr[portid]);
+		printf(", ");
+		print_ethaddr(
+			"Destination:",
+			(const struct rte_ether_addr *)&dest_eth_addr[portid]);
+		printf(", ");
+
+		/*
+		 * prepare src MACs for each port.
+		 */
+		rte_ether_addr_copy(
+			&ports_eth_addr[portid],
+			(struct rte_ether_addr *)(val_eth + portid) + 1);
+
+		/* Init memory */
+		if (!per_port_pool) {
+			/* portid = 0; this is *not* signifying the first port,
+			 * rather, it signifies that portid is ignored.
+			 */
+			ret = init_mem(0, NB_MBUF(nb_ports));
+		} else {
+			ret = init_mem(portid, NB_MBUF(1));
+		}
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "init_mem() failed\n");
+
+		/* Init one TX queue per couple (lcore,port) */
+		queueid = 0;
+		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+			if (rte_lcore_is_enabled(lcore_id) == 0)
+				continue;
+
+			qconf = &lcore_conf[lcore_id];
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
+			fflush(stdout);
+
+			txconf = &dev_info.default_txconf;
+			txconf->offloads = local_port_conf.txmode.offloads;
+			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+						     socketid, txconf);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_tx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+			queueid++;
+		}
+
+		printf("\n");
+	}
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+		qconf = &lcore_conf[lcore_id];
+		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
+		fflush(stdout);
+		/* Init RX queues */
+		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+			struct rte_eth_rxconf rxq_conf;
+
+			portid = qconf->rx_queue_list[queue].port_id;
+			queueid = qconf->rx_queue_list[queue].queue_id;
+
+			if (numa_on)
+				socketid = (uint8_t)rte_lcore_to_socket_id(
+					lcore_id);
+			else
+				socketid = 0;
+
+			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
+			fflush(stdout);
+
+			rte_eth_dev_info_get(portid, &dev_info);
+			rxq_conf = dev_info.default_rxconf;
+			rxq_conf.offloads = port_conf.rxmode.offloads;
+			if (!per_port_pool)
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf, pktmbuf_pool[0][socketid]);
+			else
+				ret = rte_eth_rx_queue_setup(
+					portid, queueid, nb_rxd, socketid,
+					&rxq_conf,
+					pktmbuf_pool[portid][socketid]);
+			if (ret < 0)
+				rte_exit(EXIT_FAILURE,
+					 "rte_eth_rx_queue_setup: err=%d, "
+					 "port=%d\n",
+					 ret, portid);
+
+		}
+	}
+
+	printf("\n");
+
+	/* Start ports */
+	RTE_ETH_FOREACH_DEV(portid)
+	{
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+
+		/* Start device */
+		ret = rte_eth_dev_start(portid);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "rte_eth_dev_start: err=%d, port=%d\n", ret,
+				 portid);
+
+		/*
+		 * If enabled, put device in promiscuous mode.
+		 * This allows IO forwarding mode to forward packets
+		 * to itself through 2 cross-connected  ports of the
+		 * target machine.
+		 */
+		if (promiscuous_on)
+			rte_eth_promiscuous_enable(portid);
+	}
+
+	printf("\n");
+
+	check_all_ports_link_status(enabled_port_mask);
+
+	/* Stop ports */
+	RTE_ETH_FOREACH_DEV(portid) {
+		if ((enabled_port_mask & (1 << portid)) == 0)
+			continue;
+		printf("Closing port %d...", portid);
+		rte_eth_dev_stop(portid);
+		rte_eth_dev_close(portid);
+		printf(" Done\n");
+	}
 	printf("Bye...\n");
 
 	return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 27/29] l3fwd-graph: add graph config and main loop
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (25 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 26/29] l3fwd-graph: add ethdev configuration changes jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 28/29] doc: add graph library programmer's guide guide jerinj
                           ` (3 subsequent siblings)
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Marko Kovacevic, Ori Kam, Bruce Richardson, Radu Nicolau,
	Akhil Goyal, Tomasz Kantecki, Sunil Kumar Kori, Pavan Nikhilesh,
	Nithin Dabilpuram
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Add graph creation, configuration logic and graph main loop.
This graph main loop is run on every slave lcore and calls
rte_graph_walk() to walk over lcore specific rte_graph.
Master core accumulates and prints graph walk stats of all the
lcore's graph's.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 examples/l3fwd-graph/main.c | 248 +++++++++++++++++++++++++++++++++++-
 1 file changed, 246 insertions(+), 2 deletions(-)

diff --git a/examples/l3fwd-graph/main.c b/examples/l3fwd-graph/main.c
index 7ea788589..40108a0d3 100644
--- a/examples/l3fwd-graph/main.c
+++ b/examples/l3fwd-graph/main.c
@@ -23,9 +23,13 @@
 #include <rte_cycles.h>
 #include <rte_eal.h>
 #include <rte_ethdev.h>
+#include <rte_graph_worker.h>
+#include <rte_launch.h>
 #include <rte_lcore.h>
 #include <rte_log.h>
 #include <rte_mempool.h>
+#include <rte_node_eth_api.h>
+#include <rte_node_ip4_api.h>
 #include <rte_per_lcore.h>
 #include <rte_string_fns.h>
 #include <rte_vect.h>
@@ -75,12 +79,17 @@ static uint32_t enabled_port_mask;
 struct lcore_rx_queue {
 	uint16_t port_id;
 	uint8_t queue_id;
+	char node_name[RTE_NODE_NAMESIZE];
 };
 
 /* Lcore conf */
 struct lcore_conf {
 	uint16_t n_rx_queue;
 	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
+
+	struct rte_graph *graph;
+	char name[RTE_GRAPH_NAMESIZE];
+	rte_graph_t graph_id;
 } __rte_cache_aligned;
 
 static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
@@ -119,6 +128,25 @@ static struct rte_eth_conf port_conf = {
 
 static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS];
 
+static struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS];
+
+struct ipv4_l3fwd_lpm_route {
+	uint32_t ip;
+	uint8_t depth;
+	uint8_t if_out;
+};
+
+#define IPV4_L3FWD_LPM_NUM_ROUTES                                              \
+	(sizeof(ipv4_l3fwd_lpm_route_array) /                                  \
+	 sizeof(ipv4_l3fwd_lpm_route_array[0]))
+/* 198.18.0.0/16 are set aside for RFC2544 benchmarking. */
+static struct ipv4_l3fwd_lpm_route ipv4_l3fwd_lpm_route_array[] = {
+	{RTE_IPV4(198, 18, 0, 0), 24, 0}, {RTE_IPV4(198, 18, 1, 0), 24, 1},
+	{RTE_IPV4(198, 18, 2, 0), 24, 2}, {RTE_IPV4(198, 18, 3, 0), 24, 3},
+	{RTE_IPV4(198, 18, 4, 0), 24, 4}, {RTE_IPV4(198, 18, 5, 0), 24, 5},
+	{RTE_IPV4(198, 18, 6, 0), 24, 6}, {RTE_IPV4(198, 18, 7, 0), 24, 7},
+};
+
 static int
 check_lcore_params(void)
 {
@@ -633,17 +661,89 @@ signal_handler(int signum)
 	}
 }
 
+static void
+print_stats(void)
+{
+	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+	const char clr[] = {27, '[', '2', 'J', '\0'};
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker_*";
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL)
+		rte_exit(EXIT_FAILURE, "Unable to create stats object\n");
+
+	while (!force_quit) {
+		/* Clear screen and move to top left */
+		printf("%s%s", clr, topLeft);
+		rte_graph_cluster_stats_get(stats, 0);
+		rte_delay_ms(1E3);
+	}
+
+	rte_graph_cluster_stats_destroy(stats);
+}
+
+/* Main processing loop */
+static int
+graph_main_loop(void *conf)
+{
+	struct lcore_conf *qconf;
+	struct rte_graph *graph;
+	uint32_t lcore_id;
+
+	RTE_SET_USED(conf);
+
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_conf[lcore_id];
+	graph = qconf->graph;
+
+	if (!graph) {
+		RTE_LOG(INFO, L3FWD_GRAPH, "Lcore %u has nothing to do\n",
+			lcore_id);
+		return 0;
+	}
+
+	RTE_LOG(INFO, L3FWD_GRAPH,
+		"Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+		qconf->name, graph);
+
+	while (likely(!force_quit))
+		rte_graph_walk(graph);
+
+	return 0;
+}
+
 int
 main(int argc, char **argv)
 {
+	/* Rewrite data of src and dst ether addr */
+	uint8_t rewrite_data[2 * sizeof(struct rte_ether_addr)];
+	static const char * const default_patterns[] = {
+		"ip4*",
+		"ethdev_tx-*",
+		"pkt_drop",
+	};
 	uint8_t nb_rx_queue, queue, socketid;
+	struct rte_graph_param graph_conf;
 	struct rte_eth_dev_info dev_info;
+	uint32_t nb_ports, nb_conf = 0;
 	uint32_t n_tx_queue, nb_lcores;
 	struct rte_eth_txconf *txconf;
-	uint16_t queueid, portid;
+	uint16_t queueid, portid, i;
+	const char **node_patterns;
 	struct lcore_conf *qconf;
+	uint16_t nb_graphs = 0;
+	uint16_t nb_patterns;
+	uint8_t rewrite_len;
 	uint32_t lcore_id;
-	uint32_t nb_ports;
 	int ret;
 
 	/* Init EAL */
@@ -792,6 +892,18 @@ main(int argc, char **argv)
 			queueid++;
 		}
 
+		/* Setup ethdev node config */
+		ethdev_conf[nb_conf].port_id = portid;
+		ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+		ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+		if (!per_port_pool)
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+
+		else
+			ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+		ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+		nb_conf++;
 		printf("\n");
 	}
 
@@ -835,11 +947,24 @@ main(int argc, char **argv)
 					 "port=%d\n",
 					 ret, portid);
 
+			/* Add this queue node to its graph */
+			snprintf(qconf->rx_queue_list[queue].node_name,
+				 RTE_NODE_NAMESIZE, "ethdev_rx-%u-%u", portid,
+				 queueid);
 		}
+
+		/* Alloc a graph to this lcore only if source exists  */
+		if (qconf->n_rx_queue)
+			nb_graphs++;
 	}
 
 	printf("\n");
 
+	/* Ethdev node config, skip rx queue mapping */
+	ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+	if (ret)
+		rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", ret);
+
 	/* Start ports */
 	RTE_ETH_FOREACH_DEV(portid)
 	{
@@ -867,6 +992,125 @@ main(int argc, char **argv)
 
 	check_all_ports_link_status(enabled_port_mask);
 
+	/* Graph Initialization */
+	nb_patterns = RTE_DIM(default_patterns);
+	node_patterns = malloc((MAX_RX_QUEUE_PER_LCORE + nb_patterns) *
+			       sizeof(*node_patterns));
+	if (!node_patterns)
+		return -ENOMEM;
+	memcpy(node_patterns, default_patterns,
+	       nb_patterns * sizeof(*node_patterns));
+
+	memset(&graph_conf, 0, sizeof(graph_conf));
+	graph_conf.node_patterns = node_patterns;
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		rte_graph_t graph_id;
+		rte_edge_t i;
+
+		if (rte_lcore_is_enabled(lcore_id) == 0)
+			continue;
+
+		qconf = &lcore_conf[lcore_id];
+
+		/* Skip graph creation if no source exists */
+		if (!qconf->n_rx_queue)
+			continue;
+
+		/* Add rx node patterns of this lcore */
+		for (i = 0; i < qconf->n_rx_queue; i++) {
+			graph_conf.node_patterns[nb_patterns + i] =
+				qconf->rx_queue_list[i].node_name;
+		}
+
+		graph_conf.nb_node_patterns = nb_patterns + i;
+		graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+		snprintf(qconf->name, sizeof(qconf->name), "worker_%u",
+			 lcore_id);
+
+		graph_id = rte_graph_create(qconf->name, &graph_conf);
+		if (graph_id == RTE_GRAPH_ID_INVALID)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_create(): graph_id invalid"
+				 " for lcore %u\n", lcore_id);
+
+		qconf->graph_id = graph_id;
+		qconf->graph = rte_graph_lookup(qconf->name);
+		if (!qconf->graph)
+			rte_exit(EXIT_FAILURE,
+				 "rte_graph_lookup(): graph %s not found\n",
+				 qconf->name);
+	}
+
+	memset(&rewrite_data, 0, sizeof(rewrite_data));
+	rewrite_len = sizeof(rewrite_data);
+
+	/* Add route to ip4 graph infra */
+	for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+		char route_str[INET6_ADDRSTRLEN * 4];
+		char abuf[INET6_ADDRSTRLEN];
+		struct in_addr in;
+		uint32_t dst_port;
+
+		/* Skip unused ports */
+		if ((1 << ipv4_l3fwd_lpm_route_array[i].if_out &
+		     enabled_port_mask) == 0)
+			continue;
+
+		dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+
+		in.s_addr = htonl(ipv4_l3fwd_lpm_route_array[i].ip);
+		snprintf(route_str, sizeof(route_str), "%s / %d (%d)",
+			 inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
+			 ipv4_l3fwd_lpm_route_array[i].depth,
+			 ipv4_l3fwd_lpm_route_array[i].if_out);
+
+		/* Use route index 'i' as next hop id */
+		ret = rte_node_ip4_route_add(
+			ipv4_l3fwd_lpm_route_array[i].ip,
+			ipv4_l3fwd_lpm_route_array[i].depth, i,
+			RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add ip4 route %s to graph\n",
+				 route_str);
+
+		memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+		/* Add next hop rewrite data for id 'i' */
+		ret = rte_node_ip4_rewrite_add(i, rewrite_data,
+					       rewrite_len, dst_port);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE,
+				 "Unable to add next hop %u for "
+				 "route %s\n", i, route_str);
+
+		RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+			route_str, i);
+	}
+
+	/* Launch per-lcore init on every slave lcore */
+	rte_eal_mp_remote_launch(graph_main_loop, NULL, SKIP_MASTER);
+
+	/* Accumulate and print stats on master until exit */
+	if (rte_graph_has_stats_feature())
+		print_stats();
+
+	/* Wait for slave cores to exit */
+	ret = 0;
+	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
+		ret = rte_eal_wait_lcore(lcore_id);
+		/* Destroy graph */
+		if (ret < 0 || rte_graph_destroy(
+			rte_graph_from_name(lcore_conf[lcore_id].name))) {
+			ret = -1;
+			break;
+		}
+	}
+	free(node_patterns);
+
 	/* Stop ports */
 	RTE_ETH_FOREACH_DEV(portid) {
 		if ((enabled_port_mask & (1 << portid)) == 0)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 28/29] doc: add graph library programmer's guide guide
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (26 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 27/29] l3fwd-graph: add graph config and main loop jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-05-11  9:27           ` David Marchand
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 29/29] doc: add l3fwd graph application user guide jerinj
                           ` (2 subsequent siblings)
  30 siblings, 1 reply; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: John McNamara, Marko Kovacevic
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang, amo, Jerin Jacob

From: Jerin Jacob <jerinj@marvell.com>

Adding programmer's guide for Graph library and the inbuilt nodes.
This patch also updates the release note for the new libraries.

Signed-off-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
---
 doc/guides/prog_guide/graph_lib.rst           |  397 ++
 .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
 .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
 doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
 doc/guides/prog_guide/index.rst               |    1 +
 doc/guides/rel_notes/release_20_05.rst        |   24 +
 6 files changed, 5532 insertions(+)
 create mode 100644 doc/guides/prog_guide/graph_lib.rst
 create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
 create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
 create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg

diff --git a/doc/guides/prog_guide/graph_lib.rst b/doc/guides/prog_guide/graph_lib.rst
new file mode 100644
index 000000000..669d77c74
--- /dev/null
+++ b/doc/guides/prog_guide/graph_lib.rst
@@ -0,0 +1,397 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+Graph Library and Inbuilt Nodes
+===============================
+
+Graph architecture abstracts the data processing functions as a ``node`` and
+``links`` them together to create a complex ``graph`` to enable reusable/modular
+data processing functions.
+
+The graph library provides API to enable graph framework operations such as
+create, lookup, dump and destroy on graph and node operations such as clone,
+edge update, and edge shrink, etc. The API also allows to create the stats
+cluster to monitor per graph and per node stats.
+
+Features
+--------
+
+Features of the Graph library are:
+
+- Nodes as plugins.
+- Support for out of tree nodes.
+- Inbuilt nodes for packet processing.
+- Multi-process support.
+- Low overhead graph walk and node enqueue.
+- Low overhead statistics collection infrastructure.
+- Support to export the graph as a Graphviz dot file. See ``rte_graph_export()``.
+- Allow having another graph walk implementation in the future by segregating
+  the fast path(``rte_graph_worker.h``) and slow path code.
+
+Advantages of Graph architecture
+--------------------------------
+
+- Memory latency is the enemy for high-speed packet processing, moving the
+  similar packet processing code to a node will reduce the I cache and D
+  caches misses.
+- Exploits the probability that most packets will follow the same nodes in the
+  graph.
+- Allow SIMD instructions for packet processing of the node.-
+- The modular scheme allows having reusable nodes for the consumers.
+- The modular scheme allows us to abstract the vendor HW specific
+  optimizations as a node.
+
+Performance tuning parameters
+-----------------------------
+
+- Test with various burst size values (256, 128, 64, 32) using
+  CONFIG_RTE_GRAPH_BURST_SIZE config option.
+  The testing shows, on x86 and arm64 servers, The sweet spot is 256 burst
+  size. While on arm64 embedded SoCs, it is either 64 or 128.
+- Disable node statistics (using ``CONFIG_RTE_LIBRTE_GRAPH_STATS`` config option)
+  if not needed.
+- Use arm64 optimized memory copy for arm64 architecture by
+  selecting ``CONFIG_RTE_ARCH_ARM64_MEMCPY``.
+
+Programming model
+-----------------
+
+Anatomy of Node:
+~~~~~~~~~~~~~~~~
+
+.. _figure_anatomy_of_a_node:
+
+.. figure:: img/anatomy_of_a_node.*
+
+The :numref:`figure_anatomy_of_a_node` diagram depicts the anatomy of a node.
+
+The node is the basic building block of the graph framework.
+
+A node consists of:
+
+process():
+^^^^^^^^^^
+
+The callback function will be invoked by worker thread using
+``rte_graph_walk()`` function when there is data to be processed by the node.
+A graph node process the function using ``process()`` and enqueue to next
+downstream node using ``rte_node_enqueue*()`` function.
+
+Context memory:
+^^^^^^^^^^^^^^^
+
+It is memory allocated by the library to store the node-specific context
+information. This memory will be used by process(), init(), fini() callbacks.
+
+init():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_create()`` on when
+a node gets attached to a graph.
+
+fini():
+^^^^^^^
+
+The callback function will be invoked by ``rte_graph_destroy()`` on when a
+node gets detached to a graph.
+
+Node name:
+^^^^^^^^^^
+
+It is the name of the node. When a node registers to graph library, the library
+gives the ID as ``rte_node_t`` type. Both ID or Name shall be used lookup the
+node. ``rte_node_from_name()``, ``rte_node_id_to_name()`` are the node
+lookup functions.
+
+nb_edges:
+^^^^^^^^^
+
+The number of downstream nodes connected to this node. The ``next_nodes[]``
+stores the downstream nodes objects. ``rte_node_edge_update()`` and
+``rte_node_edge_shrink()`` functions shall be used to update the ``next_node[]``
+objects. Consumers of the node APIs are free to update the ``next_node[]``
+objects till ``rte_graph_create()`` invoked.
+
+next_node[]:
+^^^^^^^^^^^^
+
+The dynamic array to store the downstream nodes connected to this node. Downstream
+node should not be current node itself or a source node.
+
+Source node:
+^^^^^^^^^^^^
+
+Source nodes are static nodes created using ``RTE_NODE_REGISTER`` by passing
+``flags`` as ``RTE_NODE_SOURCE_F``.
+While performing the graph walk, the ``process()`` function of all the source
+nodes will be called first. So that these nodes can be used as input nodes for a graph.
+
+Node creation and registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Node implementer creates the node by implementing ops and attributes of
+  ``struct rte_node_register``.
+
+* The library registers the node by invoking RTE_NODE_REGISTER on library load
+  using the constructor scheme. The constructor scheme used here to support multi-process.
+
+Link the Nodes to create the graph topology
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. _figure_link_the_nodes:
+
+.. figure:: img/link_the_nodes.*
+
+The :numref:`figure_link_the_nodes` diagram shows a graph topology after
+linking the N nodes.
+
+Once nodes are available to the program, Application or node public API
+functions can links them together to create a complex packet processing graph.
+
+There are multiple different types of strategies to link the nodes.
+
+Method (a):
+^^^^^^^^^^^
+Provide the ``next_nodes[]`` at the node registration time. See  ``struct rte_node_register::nb_edges``.
+This is a use case to address the static node scheme where one knows upfront the
+``next_nodes[]`` of the node.
+
+Method (b):
+^^^^^^^^^^^
+Use ``rte_node_edge_get()``, ``rte_node_edge_update()``, ``rte_node_edge_shrink()``
+to update the ``next_nodes[]`` links for the node runtime but before graph create.
+
+Method (c):
+^^^^^^^^^^^
+Use ``rte_node_clone()`` to clone a already existing node, created using RTE_NODE_REGISTER.
+When ``rte_node_clone()`` invoked, The library, would clone all the attributes
+of the node and creates a new one. The name for cloned node shall be
+``"parent_node_name-user_provided_name"``.
+
+This method enables the use case of Rx and Tx nodes where multiple of those nodes
+need to be cloned based on the number of CPU available in the system.
+The cloned nodes will be identical, except the ``"context memory"``.
+Context memory will have information of port, queue pair in case of Rx and Tx
+ethdev nodes.
+
+Create the graph object
+~~~~~~~~~~~~~~~~~~~~~~~
+Now that the nodes are linked, Its time to create a graph by including
+the required nodes. The application can provide a set of node patterns to
+form a graph object. The ``famish()`` API used underneath for the pattern
+matching to include the required nodes. After the graph create any changes to
+nodes or graph is not allowed.
+
+The ``rte_graph_create()`` API shall be used to create the graph.
+
+Example of a graph object creation:
+
+.. code-block:: console
+
+   {"ethdev_rx-0-0", ip4*, ethdev_tx-*"}
+
+In the above example, A graph object will be created with ethdev Rx
+node of port 0 and queue 0, all ipv4* nodes in the system,
+and ethdev tx node of all ports.
+
+Multicore graph processing
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+In the current graph library implementation, specifically,
+``rte_graph_walk()`` and ``rte_node_enqueue*`` fast path API functions
+are designed to work on single-core to have better performance.
+The fast path API works on graph object, So the multi-core graph
+processing strategy would be to create graph object PER WORKER.
+
+In fast path
+~~~~~~~~~~~~
+Typical fast-path code looks like below, where the application
+gets the fast-path graph object using ``rte_graph_lookup()``
+on the worker thread and run the ``rte_graph_walk()`` in a tight loop.
+
+.. code-block:: c
+
+    struct rte_graph *graph = rte_graph_lookup("worker0");
+
+    while (!done) {
+        rte_graph_walk(graph);
+    }
+
+Context update when graph walk in action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The fast-path object for the node is ``struct rte_node``.
+
+It may be possible that in slow-path or after the graph walk-in action,
+the user needs to update the context of the node hence access to
+``struct rte_node *`` memory.
+
+``rte_graph_foreach_node()``, ``rte_graph_node_get()``,
+``rte_graph_node_get_by_name()`` APIs can be used to to get the
+``struct rte_node*``. ``rte_graph_foreach_node()`` iterator function works on
+``struct rte_graph *`` fast-path graph object while others works on graph ID or name.
+
+Get the node statistics using graph cluster
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The user may need to know the aggregate stats of the node across
+multiple graph objects. Especially the situation where each graph object bound
+to a worker thread.
+
+Introduced a graph cluster object for statistics.
+``rte_graph_cluster_stats_create()`` API shall be used for creating a
+graph cluster with multiple graph objects and ``rte_graph_cluster_stats_get()``
+to get the aggregate node statistics.
+
+An example statistics output from ``rte_graph_cluster_stats_get()``
+
+.. code-block:: diff
+
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+    |Node     |calls      |objs         |realloc_count  |objs/call  |objs/sec(10E6) |cycles/call|
+    +---------------------+-------------+---------------+-----------+---------------+-----------+
+    |node0    |12977424   |3322220544   |5              |256.000    |3047.151872    |20.0000    |
+    |node1    |12977653   |3322279168   |0              |256.000    |3047.210496    |17.0000    |
+    |node2    |12977696   |3322290176   |0              |256.000    |3047.221504    |17.0000    |
+    |node3    |12977734   |3322299904   |0              |256.000    |3047.231232    |17.0000    |
+    |node4    |12977784   |3322312704   |1              |256.000    |3047.243776    |17.0000    |
+    |node5    |12977825   |3322323200   |0              |256.000    |3047.254528    |17.0000    |
+    +---------+-----------+-------------+---------------+-----------+---------------+-----------+
+
+Node writing guidelines
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``process()`` function of a node is the fast-path function and that needs
+to be written carefully to achieve max performance.
+
+Broadly speaking, there are two different types of nodes.
+
+Static nodes
+~~~~~~~~~~~~
+The first kind of nodes are those that have a fixed ``next_nodes[]`` for the
+complete burst (like ethdev_rx, ethdev_tx) and it is simple to write.
+``process()`` function can move the obj burst to the next node either using
+``rte_node_next_stream_move()`` or using ``rte_node_next_stream_get()`` and
+``rte_node_next_stream_put()``.
+
+Intermediate nodes
+~~~~~~~~~~~~~~~~~~
+The second kind of such node is ``intermediate nodes`` that decide what is the
+``next_node[]`` to send to on a per-packet basis. In these nodes,
+
+* Firstly, there has to be the best possible packet processing logic.
+
+* Secondly, each packet needs to be queued to its next node.
+
+This can be done using ``rte_node_enqueue_[x1|x2|x4]()`` APIs if
+they are to single next or ``rte_node_enqueue_next()`` that takes array of nexts.
+
+In scenario where multiple intermediate nodes are present but most of the time
+each node using the same next node for all its packets, the cost of moving every
+pointer from current node's stream to next node's stream could be avoided.
+This is called home run and ``rte_node_next_stream_move()`` could be used to
+just move stream from the current node to the next node with least number of cycles.
+Since this can be avoided only in the case where all the packets are destined
+to the same next node, node implementation should be also having worst-case
+handling where every packet could be going to different next node.
+
+Example of intermediate node implementation with home run:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1. Start with speculation that next_node = node->ctx.
+This could be the next_node application used in the previous function call of this node.
+
+2. Get the next_node stream array with required space using
+``rte_node_next_stream_get(next_node, space)``.
+
+3. while n_left_from > 0 (i.e packets left to be sent) prefetch next pkt_set
+and process current pkt_set to find their next node
+
+4. if all the next nodes of the current pkt_set match speculated next node,
+just count them as successfully speculated(``last_spec``) till now and
+continue the loop without actually moving them to the next node. else if there is
+a mismatch, copy all the pkt_set pointers that were ``last_spec`` and move the
+current pkt_set to their respective next's nodes using ``rte_enqueue_next_x1()``.
+Also, one of the next_node can be updated as speculated next_node if it is more
+probable. Finally, reset ``last_spec`` to zero.
+
+5. if n_left_from != 0 then goto 3) to process remaining packets.
+
+6. if last_spec == nb_objs, All the objects passed were successfully speculated
+to single next node. So, the current stream can be moved to next node using
+``rte_node_next_stream_move(node, next_node)``.
+This is the ``home run`` where memcpy of buffer pointers to next node is avoided.
+
+7. Update the ``node->ctx`` with more probable next node.
+
+Graph object memory layout
+--------------------------
+.. _figure_graph_mem_layout:
+
+.. figure:: img/graph_mem_layout.*
+
+The :numref:`figure_graph_mem_layout` diagram shows ``rte_graph`` object memory
+layout. Understanding the memory layout helps to debug the graph library and
+improve the performance if needed.
+
+Graph object consists of a header, circular buffer to store the pending
+stream when walking over the graph, and variable-length memory to store
+the ``rte_node`` objects.
+
+The graph_nodes_mem_create() creates and populate this memory. The functions
+such as ``rte_graph_walk()`` and ``rte_node_enqueue_*`` use this memory
+to enable fastpath services.
+
+Inbuilt Nodes
+-------------
+
+DPDK provides a set of nodes for data processing. The following section
+details the documentation for the same.
+
+ethdev_rx
+~~~~~~~~~
+This node does ``rte_eth_rx_burst()`` into stream buffer passed to it
+(src node stream) and does ``rte_node_next_stream_move()`` only when
+there are packets received. Each ``rte_node`` works only on one Rx port and
+queue that it gets from node->ctx. For each (port X, rx_queue Y),
+a rte_node is cloned from  ethdev_rx_base_node as ``ethdev_rx-X-Y`` in
+``rte_node_eth_config()`` along with updating ``node->ctx``.
+Each graph needs to be associated  with a unique rte_node for a (port, rx_queue).
+
+ethdev_tx
+~~~~~~~~~
+This node does ``rte_eth_tx_burst()`` for a burst of objs received by it.
+It sends the burst to a fixed Tx Port and Queue information from
+node->ctx. For each (port X), this ``rte_node`` is cloned from
+ethdev_tx_node_base as "ethdev_tx-X" in ``rte_node_eth_config()``
+along with updating node->context.
+
+Since each graph doesn't need more than one Txq, per port, a Txq is assigned
+based on graph id to each rte_node instance. Each graph needs to be associated
+with a rte_node for each (port).
+
+pkt_drop
+~~~~~~~~
+This node frees all the objects passed to it considering them as
+``rte_mbufs`` that need to be freed.
+
+ip4_lookup
+~~~~~~~~~~
+This node is an intermediate node that does LPM lookup for the received
+ipv4 packets and the result determines each packets next node.
+
+On successful LPM lookup, the result contains the ``next_node`` id and
+``next-hop`` id with which the packet needs to be further processed.
+
+On LPM lookup failure, objects are redirected to pkt_drop node.
+``rte_node_ip4_route_add()`` is control path API to add ipv4 routes.
+To achieve home run, node use ``rte_node_stream_move()`` as mentioned in above
+sections.
+
+ip4_rewrite
+~~~~~~~~~~~
+This node gets packets from ``ip4_lookup`` node with next-hop id for each
+packet is embedded in ``node_mbuf_priv1(mbuf)->nh``. This id is used
+to determine the L2 header to be written to the packet before sending
+the packet out to a particular ethdev_tx node.
+``rte_node_ip4_rewrite_add()`` is control path API to add next-hop info.
+
+null
+~~~~
+This node ignores the set of objects passed to it and reports that all are
+processed.
+
diff --git a/doc/guides/prog_guide/img/anatomy_of_a_node.svg b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
new file mode 100644
index 000000000..fa4b5b2d5
--- /dev/null
+++ b/doc/guides/prog_guide/img/anatomy_of_a_node.svg
@@ -0,0 +1,1078 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg419"
+   sodipodi:docname="anatomy_of_a_node.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata425">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs423" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview421"
+     showgrid="false"
+     inkscape:zoom="2.406782"
+     inkscape:cx="470.64353"
+     inkscape:cy="284.06748"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg419" />
+  <clipPath
+     id="p.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path10" />
+  </clipPath>
+  <g
+     clip-path="url(#p.0)"
+     id="g417"
+     transform="matrix(1.0160138,0,0,1.0169275,-5.7394334,-5.6337913)">
+    <path
+       d="M 0,0 H 960 V 540 H 0 Z"
+       id="path13"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path15"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 99.11286,61.721786 h 812.126 V 487.95799 h -812.126 z"
+       id="path17"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#741b47;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path19"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 139.76378,88.51181 h 541.57477 v 372.8504 H 139.76378 Z"
+       id="path21"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#c27ba0;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:4, 3, 1, 3" />
+    <path
+       d="M 540.8504,238.14069 V 415.12232"
+       id="path23"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="M 619.6588,238.14069 V 415.12232"
+       id="path25"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,238.63937 h 79.80579"
+       id="path27"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,273.8362 h 79.80579"
+       id="path29"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,309.03308 h 79.80579"
+       id="path31"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,344.22992 h 79.80579"
+       id="path33"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,379.42676 h 79.80579"
+       id="path35"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 540.3517,414.62363 h 79.80579"
+       id="path37"
+       inkscape:connector-curvature="0"
+       style="fill-rule:nonzero;stroke:#1155cc;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 559.7812,260.43936 v -9.54686 h 1.29687 l 5.01563,7.49998 v -7.49998 h 1.20312 v 9.54686 h -1.29687 l -5.01563,-7.49998 v 7.49998 z m 9.04706,-3.45312 q 0,-1.92186 1.07812,-2.84374 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57811 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28123 -0.59375,-1.93748 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98436 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03124 0.34375,-1.87499 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.
 79687,0.67188 v -3.42188 h 1.17188 v 9.54686 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42186 -0.54687,-2.07811 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,1.99999 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.74999 0.89063,-2.70311 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65623 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85936 -0.4375,-1.29686 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.43748 z m 9.89667,-0.57811 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.68
 75,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.29688 0.95313,0.89063 0.375,0.57812 0.59375,1.42187 0.21875,0.82813 0.21875,2.25 0,1.67186 -0.35938,2.70311 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.98438 -0.95313,-1.1875 -0.95313,-3.87498 z m 1.20313,0 q 0,2.34373 0.54687,3.12498 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.12498 0,-2.35937 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path39"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,295.63623 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.
 42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 H 598.128 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.1406
 2 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path41"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,330.83307 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29687 q -0.0156,-0.42187 0.14062,-0.8125 0.23438,-0.64062 0.76563,-1.26562 0.53125,-0.625
  1.53125,-1.45313 1.5625,-1.26562 2.10937,-2.01562 0.54688,-0.75 0.54688,-1.40625 0,-0.70313 -0.5,-1.17188 -0.5,-0.48437 -1.29688,-0.48437 -0.85937,0 -1.375,0.51562 -0.5,0.5 -0.5,1.39063 l -1.20312,-0.10938 q 0.125,-1.35937 0.92187,-2.0625 0.8125,-0.70312 2.17188,-0.70312 1.375,0 2.17187,0.76562 0.8125,0.75 0.8125,1.875 0,0.57813 -0.23437,1.14063 -0.23438,0.54687 -0.78125,1.15625 -0.54688,0.60937 -1.8125,1.67187 -1.04688,0.89063 -1.35938,1.21875 -0.29687,0.3125 -0.48437,0.625 z"
+       id="path43"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 577.7547,366.0299 v -1.32813 h 1.34375 v 1.32813 z m 3.703,0 v -1.32813 h 1.34375 v 1.32813 z"
+       id="path45"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 559.7812,401.22678 v -9.54687 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54687 h -1.29687 l -5.01563,-7.5 v 7.5 z m 9.04706,-3.45312 q 0,-1.92188 1.07812,-2.84375 0.89063,-0.76563 2.17188,-0.76563 1.42187,0 2.32812,0.9375 0.90625,0.92188 0.90625,2.57813 0,1.32812 -0.40625,2.09375 -0.39062,0.76562 -1.15625,1.1875 -0.76562,0.42187 -1.67187,0.42187 -1.45313,0 -2.35938,-0.92187 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32812 0.57813,1.98437 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67187 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98438 z m 11.13123,3.45312 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45312 -0.6875,-0.45313 -1.07812,-1.26563 -0.375,-0.82812 -0.375,-1.89062 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45313 1.54687,-0.45313 0.625,0 1.10938,0.26563 0.5,0.25 0.79687,0.67187 v -3.
 42187 h 1.17188 v 9.54687 z m -3.70313,-3.45312 q 0,1.32812 0.5625,1.98437 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42187 -0.54687,-2.07812 -0.54688,-0.67188 -1.34375,-0.67188 -0.78125,0 -1.3125,0.64063 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23437 1.20313,0.14063 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57812 -1.96875,0.57812 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60937 0,-1.75 0.89063,-2.70313 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10938 0,0.3125 h -5.15625 q 0.0625,1.14063 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32812 0.45313,-0.34375 0.71875,-1.07813 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85937 -0.4375,-1.29687 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54687 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98438 q 0.75,-1.14063 2.1875,-1.14063 0.625,0 1.15625,0.21875 0.53125,0.218
 75 0.78125,0.59375 0.26562,0.35938 0.375,0.85938 0.0625,0.32812 0.0625,1.14062 v 4.25 h -1.17188 v -4.20312 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35938 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79687 v 3.78125 z"
+       id="path47"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path49"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 570.17847,112.47769 h 85.98425 v 35.2126 h -85.98425 z"
+       id="path51"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 598.25494,126.88399 v -9.54688 h 1.29688 l 5.01562,7.5 v -7.5 h 1.20313 v 9.54688 h -1.29688 l -5.01562,-7.5 v 7.5 z m 9.047,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.4
 2188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z"
+       id="path53"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 596.41,142.88399 v -9.54688 h 1.29687 l 5.01563,7.5 v -7.5 h 1.20312 v 9.54688 h -1.29687 l -5.01563,-7.5 v 7.5 z m 14.00012,-0.85938 q -0.65625,0.5625 -1.26562,0.79688 -0.59375,0.21875 -1.28125,0.21875 -1.14063,0 -1.75,-0.54688 -0.60938,-0.5625 -0.60938,-1.4375 0,-0.5 0.21875,-0.92187 0.23438,-0.42188 0.60938,-0.67188 0.375,-0.25 0.84375,-0.39062 0.34375,-0.0781 1.04687,-0.17188 1.42188,-0.17187 2.09375,-0.40625 0,-0.23437 0,-0.29687 0,-0.71875 -0.32812,-1.01563 -0.45313,-0.39062 -1.34375,-0.39062 -0.8125,0 -1.21875,0.29687 -0.39063,0.28125 -0.57813,1.01563 l -1.14062,-0.15625 q 0.15625,-0.73438 0.51562,-1.1875 0.35938,-0.45313 1.03125,-0.6875 0.67188,-0.25 1.5625,-0.25 0.89063,0 1.4375,0.20312 0.5625,0.20313 0.8125,0.53125 0.26563,0.3125 0.375,0.79688 0.0469,0.29687 0.0469,1.07812 v 1.5625 q 0,1.625 0.0781,2.0625 0.0781,0.4375 0.29688,0.82813 h -1.21875 q -0.1875,-0.35938 -0.23438,-0.85938 z m -0.0937,-2.60937 q -0.64062,0.26562 -1.92187,0.4375 -0.71875,0.10937
  -1.01563,0.25 -0.29687,0.125 -0.46875,0.375 -0.15625,0.25 -0.15625,0.54687 0,0.46875 0.34375,0.78125 0.35938,0.3125 1.04688,0.3125 0.67187,0 1.20312,-0.29687 0.53125,-0.29688 0.78125,-0.8125 0.1875,-0.39063 0.1875,-1.17188 z m 2.9906,3.46875 v -6.90625 h 1.04688 v 0.96875 q 0.32812,-0.51563 0.85937,-0.8125 0.54688,-0.3125 1.23438,-0.3125 0.78125,0 1.26562,0.3125 0.48438,0.3125 0.6875,0.89062 0.82813,-1.20312 2.14063,-1.20312 1.03125,0 1.57812,0.57812 0.5625,0.5625 0.5625,1.73438 v 4.75 h -1.17187 v -4.35938 q 0,-0.70312 -0.125,-1 -0.10938,-0.3125 -0.40625,-0.5 -0.29688,-0.1875 -0.70313,-0.1875 -0.71875,0 -1.20312,0.48438 -0.48438,0.48437 -0.48438,1.54687 v 4.01563 h -1.17187 v -4.48438 q 0,-0.78125 -0.29688,-1.17187 -0.28125,-0.39063 -0.92187,-0.39063 -0.5,0 -0.92188,0.26563 -0.42187,0.25 -0.60937,0.75 -0.1875,0.5 -0.1875,1.45312 v 3.57813 z m 15.83686,-2.21875 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0
 .89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z"
+       id="path55"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path57"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="M 164.11023,161.09973 H 272.51968 V 416.65878 H 164.11023 Z"
+       id="path59"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 186.03761,297.92676 v -11.48438 h 1.28125 v 1.07813 q 0.45312,-0.64063 1.01562,-0.95313 0.57813,-0.3125 1.39063,-0.3125 1.0625,0 1.875,0.54688 0.8125,0.54687 1.21875,1.54687 0.42187,0.98438 0.42187,2.17188 0,1.28125 -0.46875,2.29687 -0.45312,1.01563 -1.32812,1.5625 -0.85938,0.54688 -1.82813,0.54688 -0.70312,0 -1.26562,-0.29688 -0.54688,-0.29687 -0.90625,-0.75 v 4.04688 z m 1.26562,-7.29688 q 0,1.60938 0.64063,2.375 0.65625,0.76563 1.57812,0.76563 0.9375,0 1.60938,-0.79688 0.67187,-0.79687 0.67187,-2.45312 0,-1.59375 -0.65625,-2.375 -0.65625,-0.79688 -1.5625,-0.79688 -0.89062,0 -1.59375,0.84375 -0.6875,0.84375 -0.6875,2.4375 z m 7.61719,4.10938 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 4.8125,-4.15625 q 0,-2.29688 1.28125,-3.
 40625 1.07813,-0.92188 2.60938,-0.92188 1.71875,0 2.79687,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48437,2.51563 -0.48438,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73438,0 -2.8125,-1.10938 -1.07813,-1.125 -1.07813,-3.23437 z m 1.45313,0 q 0,1.59375 0.6875,2.39062 0.70312,0.79688 1.75,0.79688 1.04687,0 1.73437,-0.79688 0.70313,-0.79687 0.70313,-2.4375 0,-1.53125 -0.70313,-2.32812 -0.6875,-0.79688 -1.73437,-0.79688 -1.04688,0 -1.75,0.79688 -0.6875,0.78125 -0.6875,2.375 z m 13.38281,1.10937 1.39062,0.1875 q -0.23437,1.42188 -1.17187,2.23438 -0.92188,0.8125 -2.28125,0.8125 -1.70313,0 -2.75,-1.10938 -1.03125,-1.125 -1.03125,-3.20312 0,-1.34375 0.4375,-2.34375 0.45312,-1.01563 1.35937,-1.51563 0.92188,-0.5 1.98438,-0.5 1.35937,0 2.21875,0.6875 0.85937,0.67188 1.09375,1.9375 l -1.35938,0.20313 q -0.20312,-0.82813 -0.70312,-1.25 -0.48438,-0.42188 -1.1875,-0.42188 -1.0625,0 -1.73438,0.76563 -0.65625,0.75 -0.65625,2.40625 0,1.67187 0.64063,2.4375 0.64062,0.75 1.67187,0.
 75 0.82813,0 1.375,-0.5 0.5625,-0.51563 0.70313,-1.57813 z m 8.26562,0.375 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.27344,2.46875 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 
 0.25,-0.46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 8,0 1.39062,-0.21875 q 0.10938,0.84375 0.64063,1.29688 0.54687,0.4375 1.5,0.4375 0.96875,0 1.4375,-0.39063 0.46875,-0.40625 0.46875,-0.9375 0,-0.46875 -0.40625,-0.75 -0.29688,-0.1875 -1.4375,-0.46875 -1.54688,-0.39062 -2.15625,-0.67187 -0.59375,-0.29688 -0.90625,-0.79688 -0.29688,-0.5 -0.29688,-1.10937 0,-0.5625 0.25,-1.03125 0.25,-0.
 46875 0.6875,-0.78125 0.32813,-0.25 0.89063,-0.40625 0.57812,-0.17188 1.21875,-0.17188 0.98437,0 1.71875,0.28125 0.73437,0.28125 1.07812,0.76563 0.35938,0.46875 0.5,1.28125 l -1.375,0.1875 q -0.0937,-0.64063 -0.54687,-1 -0.45313,-0.35938 -1.26563,-0.35938 -0.96875,0 -1.39062,0.32813 -0.40625,0.3125 -0.40625,0.73437 0,0.28125 0.17187,0.5 0.17188,0.21875 0.53125,0.375 0.21875,0.0781 1.25,0.35938 1.48438,0.39062 2.07813,0.65625 0.59375,0.25 0.92187,0.73437 0.34375,0.48438 0.34375,1.20313 0,0.70312 -0.42187,1.32812 -0.40625,0.60938 -1.1875,0.95313 -0.76563,0.34375 -1.73438,0.34375 -1.625,0 -2.46875,-0.67188 -0.84375,-0.67187 -1.07812,-2 z m 11.25,5.85938 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375
 ,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path61"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path63"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,161.09973 h 161.48032 v 141.7008 H 308.11023 Z"
+       id="path65"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 372.79996,224.29451 1.51562,0.375 q -0.46875,1.875 -1.71875,2.85937 -1.23437,0.98438 -3.01562,0.98438 -1.85938,0 -3.01563,-0.75 -1.15625,-0.76563 -1.76562,-2.1875 -0.60938,-1.4375 -0.60938,-3.07813 0,-1.79687 0.6875,-3.125 0.6875,-1.32812 1.9375,-2.01562 1.26563,-0.70313 2.78125,-0.70313 1.71875,0 2.89063,0.875 1.17187,0.875 1.64062,2.46875 l -1.5,0.34375 q -0.39062,-1.25 -1.15625,-1.8125 -0.75,-0.57812 -1.90625,-0.57812 -1.3125,0 -2.20312,0.64062 -0.89063,0.625 -1.25,1.70313 -0.35938,1.0625 -0.35938,2.1875 0,1.46875 0.42188,2.5625 0.4375,1.07812 1.32812,1.625 0.90625,0.53125 1.95313,0.53125 1.26562,0 2.14062,-0.73438 0.89063,-0.73437 1.20313,-2.17187 z m 2.67969,-0.14063 q 0,-2.29687 1.28125,-3.40625 1.07812,-0.92187 2.60937,-0.92187 1.71875,0 2.79688,1.125 1.09375,1.10937 1.09375,3.09375 0,1.59375 -0.48438,2.51562 -0.48437,0.92188 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10937 -1.07812,-1.125 -1.07812,-3.23438 z m 1.45312,0 q 0,1.59375 0.6875,2
 .39063 0.70313,0.79687 1.75,0.79687 1.04688,0 1.73438,-0.79687 0.70312,-0.79688 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32813 -0.6875,-0.79687 -1.73438,-0.79687 -1.04687,0 -1.75,0.79687 -0.6875,0.78125 -0.6875,2.375 z m 7.97656,4.15625 v -8.29687 h 1.26563 v 1.17187 q 0.90625,-1.35937 2.64062,-1.35937 0.75,0 1.375,0.26562 0.625,0.26563 0.9375,0.70313 0.3125,0.4375 0.4375,1.04687 0.0781,0.39063 0.0781,1.35938 v 5.10937 h -1.40625 v -5.04687 q 0,-0.85938 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57812 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 11.96094,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.59375,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 
 0.60937,-0.0625 z m 7.05469,-1.40625 1.45312,0.17187 q -0.34375,1.28125 -1.28125,1.98438 -0.92187,0.70312 -2.35937,0.70312 -1.82813,0 -2.89063,-1.125 -1.0625,-1.125 -1.0625,-3.14062 0,-2.09375 1.07813,-3.25 1.07812,-1.15625 2.79687,-1.15625 1.65625,0 2.70313,1.14062 1.0625,1.125 1.0625,3.17188 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76562,2.10937 0.70313,0.71875 1.73438,0.71875 0.78125,0 1.32812,-0.40625 0.54688,-0.40625 0.85938,-1.29687 z m -4.60938,-2.28125 h 4.625 q -0.0937,-1.04688 -0.53125,-1.5625 -0.67187,-0.8125 -1.73437,-0.8125 -0.96875,0 -1.64063,0.65625 -0.65625,0.64062 -0.71875,1.71875 z m 6.89844,4.95312 3.03125,-4.3125 -2.8125,-3.98437 h 1.76563 l 1.26562,1.9375 q 0.35938,0.5625 0.57813,0.9375 0.34375,-0.51563 0.64062,-0.92188 l 1.39063,-1.95312 h 1.6875 l -2.875,3.90625 3.09375,4.39062 h -1.73438 l -1.70312,-2.57812 -0.45313,-0.70313 -2.17187,3.28125 z m 12,-1.26562 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23438 -0.42188,-0.25 -0.593
 75,-0.64062 -0.17188,-0.40625 -0.17188,-1.67188 v -4.76562 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60937 0.0625,0.78125 0.0781,0.17187 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z"
+       id="path67"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 364.5812,247.31013 V 235.857 h 2.28125 l 2.71875,8.10938 q 0.375,1.125 0.54688,1.6875 0.1875,-0.625 0.60937,-1.82813 l 2.73438,-7.96875 h 2.04687 v 11.45313 h -1.46875 v -9.59375 l -3.32812,9.59375 h -1.35938 l -3.3125,-9.75 v 9.75 z m 18.875,-2.67188 1.45313,0.17188 q -0.34375,1.28125 -1.28125,1.98437 -0.92188,0.70313 -2.35938,0.70313 -1.82812,0 -2.89062,-1.125 -1.0625,-1.125 -1.0625,-3.14063 0,-2.09375 1.07812,-3.25 1.07813,-1.15625 2.79688,-1.15625 1.65625,0 2.70312,1.14063 1.0625,1.125 1.0625,3.17187 0,0.125 0,0.375 h -6.1875 q 0.0781,1.375 0.76563,2.10938 0.70312,0.71875 1.73437,0.71875 0.78125,0 1.32813,-0.40625 0.54687,-0.40625 0.85937,-1.29688 z m -4.60937,-2.28125 h 4.625 q -0.0937,-1.04687 -0.53125,-1.5625 -0.67188,-0.8125 -1.73438,-0.8125 -0.96875,0 -1.64062,0.65625 -0.65625,0.64063 -0.71875,1.71875 z m 7.83593,4.95313 v -8.29688 h 1.25 v 1.15625 q 0.39063,-0.60937 1.03125,-0.96875 0.65625,-0.375 1.48438,-0.375 0.92187,0 1.51562,0.39063 0.59375,0.375 0
 .82813,1.0625 0.98437,-1.45313 2.5625,-1.45313 1.23437,0 1.89062,0.6875 0.67188,0.67188 0.67188,2.09375 v 5.70313 h -1.39063 v -5.23438 q 0,-0.84375 -0.14062,-1.20312 -0.14063,-0.375 -0.5,-0.59375 -0.35938,-0.23438 -0.84375,-0.23438 -0.875,0 -1.45313,0.57813 -0.57812,0.57812 -0.57812,1.85937 v 4.82813 h -1.40625 v -5.39063 q 0,-0.9375 -0.34375,-1.40625 -0.34375,-0.46875 -1.125,-0.46875 -0.59375,0 -1.09375,0.3125 -0.5,0.3125 -0.73438,0.92188 -0.21875,0.59375 -0.21875,1.71875 v 4.3125 z m 12.79688,-4.15625 q 0,-2.29688 1.28125,-3.40625 1.07812,-0.92188 2.60937,-0.92188 1.71875,0 2.79688,1.125 1.09375,1.10938 1.09375,3.09375 0,1.59375 -0.48438,2.51563 -0.48437,0.92187 -1.40625,1.4375 -0.90625,0.5 -2,0.5 -1.73437,0 -2.8125,-1.10938 -1.07812,-1.125 -1.07812,-3.23437 z m 1.45312,0 q 0,1.59375 0.6875,2.39062 0.70313,0.79688 1.75,0.79688 1.04688,0 1.73438,-0.79688 0.70312,-0.79687 0.70312,-2.4375 0,-1.53125 -0.70312,-2.32812 -0.6875,-0.79688 -1.73438,-0.79688 -1.04687,0 -1.75,0.7968
 8 -0.6875,0.78125 -0.6875,2.375 z m 7.96094,4.15625 v -8.29688 h 1.26563 v 1.25 q 0.48437,-0.875 0.89062,-1.15625 0.40625,-0.28125 0.90625,-0.28125 0.70313,0 1.4375,0.45313 l -0.48437,1.29687 q -0.51563,-0.29687 -1.03125,-0.29687 -0.45313,0 -0.82813,0.28125 -0.35937,0.26562 -0.51562,0.76562 -0.23438,0.75 -0.23438,1.64063 v 4.34375 z m 5.28125,3.20312 -0.15625,-1.32812 q 0.45313,0.125 0.79688,0.125 0.46875,0 0.75,-0.15625 0.28125,-0.15625 0.46875,-0.4375 0.125,-0.20313 0.42187,-1.04688 0.0469,-0.10937 0.125,-0.34375 l -3.14062,-8.3125 h 1.51562 l 1.71875,4.79688 q 0.34375,0.92187 0.60938,1.92187 0.23437,-0.96875 0.57812,-1.89062 l 1.76563,-4.82813 h 1.40625 l -3.15625,8.4375 q -0.5,1.375 -0.78125,1.89063 -0.375,0.6875 -0.85938,1.01562 -0.48437,0.32813 -1.15625,0.32813 -0.40625,0 -0.90625,-0.17188 z"
+       id="path69"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path71"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 308.11023,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path73"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 330.8464,371.3732 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55469,0 v -8.29688 h 1.26562 v 1.17188 q 0.90625,-1.35938 2.64063,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17187,-1.28125 -0.15625,-0.4375 -0.57813,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64062,0.5625 -0.64062,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.61718,-1.26563 0.20313,1.25 q -0.59375,0.125 -1.0625,0.125 -0.76563,0 -1.1875,-0.23437 -0.42188,-0.25 -0.59375,-0.64063 -0.17188,-0.40625 -0.17188,-1.67187 v -4.76563 h -1.03125 v -1.09375 h 1.03125 v -2.0625 l 1.40625,-0.84375 v 2.90625 h 1.40625 v 1.09375 h -1.40625 v 4.84375 q 0,0.60938 0.0625,0.78125 0.0781,0.17188 0.25,0.28125 0.17188,0.0937 0.48438,0.0937 0.23437,0 0.60937,-0.0625 z m
  4.07032,4.64063 q -1.17188,-1.46875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path75"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path77"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 395.14435,334.86877 h 74.4567 v 80.97638 h -74.4567 z"
+       id="path79"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 418.20865,381.21695 v -7.20313 h -1.23438 v -1.09375 h 1.23438 v -0.89062 q 0,-0.82813 0.15625,-1.23438 0.20312,-0.54687 0.70312,-0.89062 0.51563,-0.34375 1.4375,-0.34375 0.59375,0 1.3125,0.14062 l -0.20312,1.23438 q -0.4375,-0.0781 -0.82813,-0.0781 -0.64062,0 -0.90625,0.28125 -0.26562,0.26563 -0.26562,1.01563 v 0.76562 h 1.60937 v 1.09375 h -1.60937 v 7.20313 z m 4.11719,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 3.55468,0 v -8.29688 h 1.26563 v 1.17188 q 0.90625,-1.35938 2.64062,-1.35938 0.75,0 1.375,0.26563 0.625,0.26562 0.9375,0.70312 0.3125,0.4375 0.4375,1.04688 0.0781,0.39062 0.0781,1.35937 v 5.10938 h -1.40625 v -5.04688 q 0,-0.85937 -0.17188,-1.28125 -0.15625,-0.4375 -0.57812,-0.6875 -0.40625,-0.25 -0.96875,-0.25 -0.90625,0 -1.5625,0.57813 -0.64063,0.5625 -0.64063,2.15625 v 4.53125 z m 8.89844,-9.84375 v -1.60938 h 1.40625 v 1.60938 z m 0,9.84375 v -8.29688 h 1.40625 v 8.29688 z m 6.24219,3.375 q -1.17188,-1.4
 6875 -1.98438,-3.4375 -0.79687,-1.98438 -0.79687,-4.09375 0,-1.85938 0.60937,-3.5625 0.70313,-1.96875 2.17188,-3.9375 h 1 q -0.9375,1.625 -1.25,2.32812 -0.46875,1.07813 -0.75,2.25 -0.32813,1.45313 -0.32813,2.9375 0,3.75 2.32813,7.51563 z m 3.5625,0 h -1.01563 q 2.34375,-3.76563 2.34375,-7.51563 0,-1.46875 -0.34375,-2.92187 -0.26562,-1.17188 -0.73437,-2.25 -0.3125,-0.70313 -1.26563,-2.34375 h 1.01563 q 1.46875,1.96875 2.17187,3.9375 0.59375,1.70312 0.59375,3.5625 0,2.10937 -0.8125,4.09375 -0.79687,1.96875 -1.95312,3.4375 z"
+       id="path81"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path83"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 272.51968,240.83989 6.33072,-6.3307 v 3.16536 h 22.92914 v -3.16536 l 6.33069,6.3307 -6.33069,6.33072 v -3.16536 H 278.8504 v 3.16536 z"
+       id="path85"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path87"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 344.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path89"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path91"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 424.71915,308.78217 3.16537,-3.16537 3.16535,3.16537 h -1.58267 v 18.86612 h 1.58267 l -3.16535,3.16537 -3.16537,-3.16537 h 1.5827 v -18.86612 z"
+       id="path93"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 532.8373,210.97375 h 99.40155 v 27.46457 H 532.8373 Z"
+       id="path95"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 542.7123,232.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.14062,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 12.14685,-2.21875 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60
 938,1.4375 z m 5.7406,4.125 2.53125,-3.59375 -2.34375,-3.3125 h 1.46875 l 1.0625,1.60937 q 0.29688,0.46875 0.48438,0.78125 0.28125,-0.4375 0.51562,-0.76562 l 1.17188,-1.625 h 1.40625 l -2.39063,3.25 2.5625,3.65625 h -1.4375 l -1.42187,-2.14063 -0.375,-0.59375 -1.8125,2.73438 z m 10.00781,-1.04688 0.17188,1.03125 q -0.5,0.10938 -0.89063,0.10938 -0.64062,0 -1,-0.20313 -0.34375,-0.20312 -0.48437,-0.53125 -0.14063,-0.32812 -0.14063,-1.39062 v -3.96875 h -0.85937 v -0.90625 h 0.85937 v -1.71875 l 1.17188,-0.70313 v 2.42188 h 1.17187 v 0.90625 h -1.17187 v 4.04687 q 0,0.5 0.0469,0.64063 0.0625,0.14062 0.20313,0.23437 0.14062,0.0781 0.40625,0.0781 0.20312,0 0.51562,-0.0469 z m 0.0624,3.70313 v -0.85938 h 7.76563 v 0.85938 z m 8.4906,-2.65625 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26563,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17187 v -4.20313 q 0,-0.71875 -0.14063,-1.0625 -0.1406
 2,-0.35937 -0.48437,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29688,0.46875 -0.54687,0.46875 -0.54687,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188
  v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.36561,1.23438 1.20312,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76562,0.57813 -1.96875,0.57813 -1.51562,0 -2.40625,-0.9375 -0.89062,-0.9375 -0.89062,-2.60938 0,-1.75 0.89062,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39063,0 2.26563,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64062,1.75 0.57813,0.59375 1.4375,0.59375 0.65625,0 1.10938,-0.32813 0.45312,-0.34375 0.71875,-1.07812 z m -3.84375,-1.90625 h 3.85937 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45312,-0.6875 -0.8125,0 -1.35938,0.54688 -0.54687,0.53125 -0.60937,1.4375 z m 6.0531,2.0625 1.15625,-0.1875 q 0.10937,0.70312 0.54687,1.07812 0.45313,0.35938 1.25,0.35938 0.8125,0 1.20
 313,-0.32813 0.39062,-0.32812 0.39062,-0.76562 0,-0.39063 -0.35937,-0.625 -0.23438,-0.15625 -1.1875,-0.39063 -1.29688,-0.32812 -1.79688,-0.5625 -0.48437,-0.25 -0.75,-0.65625 -0.25,-0.42187 -0.25,-0.9375 0,-0.45312 0.20313,-0.84375 0.21875,-0.40625 0.57812,-0.67187 0.28125,-0.1875 0.75,-0.32813 0.46875,-0.14062 1.01563,-0.14062 0.8125,0 1.42187,0.23437 0.60938,0.23438 0.90625,0.64063 0.29688,0.39062 0.40625,1.0625 l -1.14062,0.15625 q -0.0781,-0.53125 -0.45313,-0.82813 -0.375,-0.3125 -1.0625,-0.3125 -0.8125,0 -1.15625,0.26563 -0.34375,0.26562 -0.34375,0.625 0,0.23437 0.14063,0.42187 0.15625,0.1875 0.45312,0.3125 0.17188,0.0625 1.03125,0.29688 1.25,0.32812 1.73438,0.54687 0.5,0.20313 0.78125,0.60938 0.28125,0.40625 0.28125,1 0,0.59375 -0.34375,1.10937 -0.34375,0.51563 -1,0.79688 -0.64063,0.28125 -1.45313,0.28125 -1.34375,0 -2.04687,-0.5625 -0.70313,-0.5625 -0.90625,-1.65625 z m 7.16406,4.71875 v -12.20313 h 2.57812 v 0.96875 h -1.40625 v 10.25 h 1.40625 v 0.98438 z m 5.64044,0
  h -2.59375 v -0.98438 h 1.42188 v -10.25 h -1.42188 v -0.96875 h 2.59375 z"
+       id="path97"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789 z"
+       id="path99"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path101"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 526.56824,415.85303 c -7.18402,0 -13.00787,-0.97061 -13.00787,-2.16791 v -85.33349 c 0,-1.1973 -5.82383,-2.16791 -13.00788,-2.16791 v 0 c 7.18405,0 13.00788,-0.97058 13.00788,-2.16788 v -85.33351 0 c 0,-1.19729 5.82385,-2.16789 13.00787,-2.16789"
+       id="path103"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 445.60104,298.39108 h 99.40158 v 27.46457 h -99.40158 z"
+       id="path105"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 455.39792,318.91107 v -6.21875 h 0.9375 v 0.875 q 0.6875,-1.01563 1.98437,-1.01563 0.5625,0 1.03125,0.20313 0.48438,0.20312 0.71875,0.53125 0.23438,0.32812 0.32813,0.76562 0.0469,0.29688 0.0469,1.03125 v 3.82813 h -1.04687 v -3.78125 q 0,-0.65625 -0.125,-0.96875 -0.125,-0.3125 -0.4375,-0.5 -0.3125,-0.20313 -0.73438,-0.20313 -0.67187,0 -1.17187,0.4375 -0.48438,0.42188 -0.48438,1.60938 v 3.40625 z m 7.64258,0 h -0.98438 v -8.59375 h 1.0625 v 3.0625 q 0.67188,-0.82813 1.70313,-0.82813 0.57812,0 1.07812,0.23438 0.51563,0.21875 0.84375,0.64062 0.34375,0.42188 0.53125,1.01563 0.1875,0.59375 0.1875,1.26562 0,1.59375 -0.79687,2.46875 -0.79688,0.875 -1.89063,0.875 -1.10937,0 -1.73437,-0.92187 z m -0.0156,-3.15625 q 0,1.10937 0.3125,1.60937 0.5,0.8125 1.34375,0.8125 0.6875,0 1.1875,-0.59375 0.51563,-0.59375 0.51563,-1.79687 0,-1.21875 -0.48438,-1.79688 -0.48437,-0.57812 -1.17187,-0.57812 -0.6875,0 -1.20313,0.60937 -0.5,0.59375 -0.5,1.73438 z m 4.73633,5.54687 v -0.76562 h 
 7 v 0.76562 z m 11.9082,-4.39062 1.09375,0.125 q -0.25,0.95312 -0.95312,1.48437 -0.70313,0.53125 -1.78125,0.53125 -1.35938,0 -2.17188,-0.84375 -0.79687,-0.84375 -0.79687,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79687,0.84375 0.79687,2.39063 0,0.0937 0,0.28125 h -4.64062 q 0.0625,1.03125 0.57812,1.57812 0.51563,0.53125 1.29688,0.53125 0.57812,0 0.98437,-0.29687 0.42188,-0.3125 0.65625,-0.96875 z m -3.45312,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39063,-1.1875 -0.51562,-0.60937 -1.3125,-0.60937 -0.73437,0 -1.23437,0.48437 -0.48438,0.48438 -0.53125,1.3125 z m 9.9082,3.70313 v -0.78125 q -0.59375,0.92187 -1.73437,0.92187 -0.75,0 -1.375,-0.40625 -0.625,-0.42187 -0.96875,-1.15625 -0.34375,-0.73437 -0.34375,-1.6875 0,-0.92187 0.3125,-1.6875 0.3125,-0.76562 0.9375,-1.15625 0.625,-0.40625 1.39062,-0.40625 0.5625,0 1,0.23438 0.4375,0.23437 0.71875,0.60937 v -3.07812 h 1.04688 v 8.59375 z m -3.32812,-3.10938 q 0,1.20313 0.5,1.79688 0.5,0
 .57812 1.1875,0.57812 0.6875,0 1.17187,-0.5625 0.48438,-0.5625 0.48438,-1.71875 0,-1.28125 -0.5,-1.875 -0.48438,-0.59375 -1.20313,-0.59375 -0.70312,0 -1.17187,0.57813 -0.46875,0.5625 -0.46875,1.79687 z m 5.76758,3.625 1.03125,0.15625 q 0.0625,0.46875 0.35937,0.6875 0.39063,0.29688 1.0625,0.29688 0.73438,0 1.125,-0.29688 0.40625,-0.29687 0.54688,-0.8125 0.0937,-0.32812 0.0781,-1.35937 -0.6875,0.8125 -1.71875,0.8125 -1.28125,0 -1.98437,-0.92188 -0.70313,-0.9375 -0.70313,-2.21875 0,-0.89062 0.3125,-1.64062 0.32813,-0.76563 0.9375,-1.17188 0.60938,-0.40625 1.4375,-0.40625 1.10938,0 1.82813,0.89063 v -0.75 h 0.96875 v 5.375 q 0,1.45312 -0.29688,2.0625 -0.29687,0.60937 -0.9375,0.95312 -0.64062,0.35938 -1.57812,0.35938 -1.10938,0 -1.79688,-0.5 -0.6875,-0.5 -0.67187,-1.51563 z m 0.875,-3.73437 q 0,1.21875 0.48437,1.78125 0.48438,0.5625 1.21875,0.5625 0.73438,0 1.21875,-0.5625 0.5,-0.5625 0.5,-1.75 0,-1.14063 -0.51562,-1.71875 -0.5,-0.57813 -1.21875,-0.57813 -0.70313,0 -1.20313,0.578
 13 -0.48437,0.5625 -0.48437,1.6875 z m 10.25195,1.21875 1.09375,0.125 q -0.25,0.95312 -0.95313,1.48437 -0.70312,0.53125 -1.78125,0.53125 -1.35937,0 -2.17187,-0.84375 -0.79688,-0.84375 -0.79688,-2.35937 0,-1.5625 0.8125,-2.42188 0.8125,-0.875 2.09375,-0.875 1.25,0 2.03125,0.84375 0.79688,0.84375 0.79688,2.39063 0,0.0937 0,0.28125 h -4.64063 q 0.0625,1.03125 0.57813,1.57812 0.51562,0.53125 1.29687,0.53125 0.57813,0 0.98438,-0.29687 0.42187,-0.3125 0.65625,-0.96875 z m -3.45313,-1.70313 h 3.46875 q -0.0625,-0.79687 -0.39062,-1.1875 -0.51563,-0.60937 -1.3125,-0.60937 -0.73438,0 -1.23438,0.48437 -0.48437,0.48438 -0.53125,1.3125 z m 5.45508,1.84375 1.03125,-0.15625 q 0.0937,0.625 0.48438,0.95313 0.40625,0.32812 1.14062,0.32812 0.71875,0 1.0625,-0.28125 0.35938,-0.29687 0.35938,-0.70312 0,-0.35938 -0.3125,-0.5625 -0.21875,-0.14063 -1.07813,-0.35938 -1.15625,-0.29687 -1.60937,-0.5 -0.4375,-0.21875 -0.67188,-0.59375 -0.23437,-0.375 -0.23437,-0.84375 0,-0.40625 0.1875,-0.76562 0.1875,
 -0.35938 0.51562,-0.59375 0.25,-0.17188 0.67188,-0.29688 0.42187,-0.125 0.92187,-0.125 0.71875,0 1.26563,0.21875 0.5625,0.20313 0.82812,0.5625 0.26563,0.35938 0.35938,0.95313 l -1.03125,0.14062 q -0.0625,-0.46875 -0.40625,-0.73437 -0.32813,-0.28125 -0.95313,-0.28125 -0.71875,0 -1.03125,0.25 -0.3125,0.23437 -0.3125,0.5625 0,0.20312 0.125,0.35937 0.14063,0.17188 0.40625,0.28125 0.15625,0.0625 0.9375,0.26563 1.125,0.3125 1.5625,0.5 0.4375,0.1875 0.6875,0.54687 0.25,0.35938 0.25,0.90625 0,0.53125 -0.3125,1 -0.29687,0.45313 -0.875,0.71875 -0.57812,0.25 -1.3125,0.25 -1.21875,0 -1.85937,-0.5 -0.625,-0.51562 -0.79688,-1.5 z"
+       id="path107"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="M 89.115486,28.062992 H 289.55644 V 55.527557 H 89.115486 Z"
+       id="path109"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 98.084236,54.98299 5.125004,-13.359375 h 1.90625 l 5.46875,13.359375 h -2.01563 l -1.54687,-4.046875 h -5.59375 l -1.468754,4.046875 z m 3.859374,-5.484375 h 4.53125 l -1.40625,-3.703125 q -0.625,-1.6875 -0.9375,-2.765625 -0.26562,1.28125 -0.71875,2.546875 z m 9.84982,5.484375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07813,-1.59375 0.875,0 1.60937,0.3125 0.73438,0.3125 1.09375,0.828125 0.375,0.5 0.51563,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64063 v -5.890625 q 0,-1 -0.20312,-1.484375 -0.1875,-0.5 -0.67188,-0.796875 -0.48437,-0.296875 -1.14062,-0.296875 -1.04688,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 16.68821,-1.1875 q -0.92186,0.765625 -1.76561,1.09375 -0.82813,0.3125 -1.79688,0.3125 -1.59375,0 -2.45312,-0.78125 -0.85938,-0.78125 -0.85938,-1.984375 0,-0.71875 0.32813,-1.296875 0.32812,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45312,-0.25 1.98438,-0.234375 2.92187,-0.5625 0.0156,-
 0.34375 0.0156,-0.421875 0,-1 -0.46874,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70312,0.40625 -0.54688,0.40625 -0.8125,1.421875 l -1.60938,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45313,-0.984375 0.95312,-0.34375 2.1875,-0.34375 1.25,0 2.01561,0.296875 0.78125,0.28125 1.14063,0.734375 0.375,0.4375 0.51562,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10937,2.890625 0.10938,0.59375 0.40625,1.15625 h -1.70312 q -0.26563,-0.515625 -0.32813,-1.1875 z m -0.14062,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01562,0.140625 -1.4375,0.328125 -0.42187,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45313,0.4375 0.9375,0 1.67187,-0.40625 0.75,-0.421875 1.09374,-1.140625 0.26563,-0.5625 0.26563,-1.640625 z m 7.78197,3.390625 0.23437,1.453125 q -0.6875,0.140625 -1.23437,0.140625 -0.89063,0 -1.39063,-0.28125 -0.48437,-0.28125 -0.6875,-0.734375 -0.20312,-0.46875 -0.20312,-1.9375 V 46.57674
  h -1.20313 v -1.265625 h 1.20313 V 42.92049 l 1.625,-0.984375 v 3.375 h 1.65625 v 1.265625 h -1.65625 v 5.671875 q 0,0.6875 0.0781,0.890625 0.0937,0.203125 0.28125,0.328125 0.20313,0.109375 0.57813,0.109375 0.26562,0 0.71875,-0.0625 z m 0.9958,-3.375 q 0,-2.6875 1.48437,-3.96875 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.29759,4.84375 v -9.671875 h 1.46875 v 1.359375 q 0.45313,-0.71875 1.20313,-1.140625 0.76562,-0.4375 1.71875,-0.4375 1.07812,0 1.76562,0.453125 0.6875,0.4375 0.96875,1.234375 1.15625,-1.6875 2.98438,-1
 .6875 1.45312,0 2.21875,0.796875 0.78125,0.796875 0.78125,2.453125 v 6.640625 h -1.64063 v -6.09375 q 0,-0.984375 -0.15625,-1.40625 -0.15625,-0.4375 -0.57812,-0.703125 -0.42188,-0.265625 -0.98438,-0.265625 -1.01562,0 -1.6875,0.6875 -0.67187,0.671875 -0.67187,2.15625 v 5.625 h -1.64063 v -6.28125 q 0,-1.09375 -0.40625,-1.640625 -0.40625,-0.546875 -1.3125,-0.546875 -0.6875,0 -1.28125,0.359375 -0.59375,0.359375 -0.85937,1.0625 -0.25,0.703125 -0.25,2.03125 v 5.015625 z m 15.46268,3.71875 -0.1875,-1.53125 q 0.54687,0.140625 0.9375,0.140625 0.54687,0 0.875,-0.1875 0.32812,-0.171875 0.54687,-0.5 0.15625,-0.25 0.5,-1.21875 0.0469,-0.140625 0.14063,-0.40625 l -3.67188,-9.6875 h 1.76563 l 2.01562,5.59375 q 0.39063,1.078125 0.70313,2.25 0.28125,-1.125 0.67187,-2.203125 l 2.07813,-5.640625 h 1.64062 l -3.6875,9.828125 q -0.59375,1.609375 -0.92187,2.203125 -0.4375,0.8125 -1,1.1875 -0.5625,0.375 -1.34375,0.375 -0.48438,0 -1.0625,-0.203125 z m 13.98018,-8.5625 q 0,-2.6875 1.48437,-3.96875 
 1.25,-1.078125 3.04688,-1.078125 2,0 3.26562,1.3125 1.26563,1.296875 1.26563,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64063,1.65625 -1.0625,0.59375 -2.32812,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79687,2.796875 0.8125,0.921875 2.04688,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23438,0 -2.04688,0.921875 -0.79687,0.90625 -0.79687,2.765625 z m 9.68821,4.84375 v -8.40625 h -1.45312 v -1.265625 h 1.45312 v -1.03125 q 0,-0.96875 0.17188,-1.453125 0.23437,-0.640625 0.82812,-1.03125 0.59375,-0.390625 1.67188,-0.390625 0.6875,0 1.53125,0.15625 l -0.25,1.4375 q -0.5,-0.09375 -0.95313,-0.09375 -0.75,0 -1.0625,0.328125 -0.3125,0.3125 -0.3125,1.1875 v 0.890625 h 1.89063 v 1.265625 h -1.89063 v 8.40625 z m 16.28849,-1.1875 q -0.92188,0.765625 -1.76563,1.09375 -0.82812,0.3125 -1.79687,0.3125 -1.59375,0 -2.45313,-0.78125 -0.85937,
 -0.78125 -0.85937,-1.984375 0,-0.71875 0.32812,-1.296875 0.32813,-0.59375 0.84375,-0.9375 0.53125,-0.359375 1.1875,-0.546875 0.46875,-0.125 1.45313,-0.25 1.98437,-0.234375 2.92187,-0.5625 0.0156,-0.34375 0.0156,-0.421875 0,-1 -0.46875,-1.421875 -0.625,-0.546875 -1.875,-0.546875 -1.15625,0 -1.70313,0.40625 -0.54687,0.40625 -0.8125,1.421875 l -1.60937,-0.21875 q 0.21875,-1.015625 0.71875,-1.640625 0.5,-0.640625 1.45312,-0.984375 0.95313,-0.34375 2.1875,-0.34375 1.25,0 2.01563,0.296875 0.78125,0.28125 1.14062,0.734375 0.375,0.4375 0.51563,1.109375 0.0781,0.421875 0.0781,1.515625 v 2.1875 q 0,2.28125 0.10938,2.890625 0.10937,0.59375 0.40625,1.15625 h -1.70313 q -0.26562,-0.515625 -0.32812,-1.1875 z m -0.14063,-3.671875 q -0.89062,0.375 -2.67187,0.625 -1.01563,0.140625 -1.4375,0.328125 -0.42188,0.1875 -0.65625,0.53125 -0.21875,0.34375 -0.21875,0.78125 0,0.65625 0.5,1.09375 0.5,0.4375 1.45312,0.4375 0.9375,0 1.67188,-0.40625 0.75,-0.421875 1.09375,-1.140625 0.26562,-0.5625 0.26562
 ,-1.640625 z m 9.38715,4.859375 v -9.671875 h 1.46875 v 1.375 q 1.0625,-1.59375 3.07812,-1.59375 0.875,0 1.60938,0.3125 0.73437,0.3125 1.09375,0.828125 0.375,0.5 0.51562,1.203125 0.0937,0.453125 0.0937,1.59375 v 5.953125 h -1.64062 v -5.890625 q 0,-1 -0.20313,-1.484375 -0.1875,-0.5 -0.67187,-0.796875 -0.48438,-0.296875 -1.14063,-0.296875 -1.04687,0 -1.8125,0.671875 -0.75,0.65625 -0.75,2.515625 v 5.28125 z m 9.76634,-4.84375 q 0,-2.6875 1.48438,-3.96875 1.25,-1.078125 3.04687,-1.078125 2,0 3.26563,1.3125 1.26562,1.296875 1.26562,3.609375 0,1.859375 -0.5625,2.9375 -0.5625,1.0625 -1.64062,1.65625 -1.0625,0.59375 -2.32813,0.59375 -2.03125,0 -3.28125,-1.296875 -1.25,-1.3125 -1.25,-3.765625 z m 1.6875,0 q 0,1.859375 0.79688,2.796875 0.8125,0.921875 2.04687,0.921875 1.21875,0 2.03125,-0.921875 0.8125,-0.9375 0.8125,-2.84375 0,-1.796875 -0.8125,-2.71875 -0.8125,-0.921875 -2.03125,-0.921875 -1.23437,0 -2.04687,0.921875 -0.79688,0.90625 -0.79688,2.765625 z m 15.56322,4.84375 v -1.2187
 5 q -0.90625,1.4375 -2.70313,1.4375 -1.15625,0 -2.125,-0.640625 -0.96875,-0.640625 -1.5,-1.78125 -0.53125,-1.140625 -0.53125,-2.625 0,-1.453125 0.48438,-2.625 0.48437,-1.1875 1.4375,-1.8125 0.96875,-0.625 2.17187,-0.625 0.875,0 1.54688,0.375 0.6875,0.359375 1.10937,0.953125 v -4.796875 h 1.64063 V 54.98299 Z m -5.17188,-4.828125 q 0,1.859375 0.78125,2.78125 0.78125,0.921875 1.84375,0.921875 1.07813,0 1.82813,-0.875 0.75,-0.890625 0.75,-2.6875 0,-1.984375 -0.76563,-2.90625 -0.76562,-0.9375 -1.89062,-0.9375 -1.07813,0 -1.8125,0.890625 -0.73438,0.890625 -0.73438,2.8125 z m 15.90697,1.71875 1.6875,0.203125 q -0.40625,1.484375 -1.48438,2.3125 -1.07812,0.8125 -2.76562,0.8125 -2.125,0 -3.375,-1.296875 -1.23438,-1.3125 -1.23438,-3.671875 0,-2.453125 1.25,-3.796875 1.26563,-1.34375 3.26563,-1.34375 1.9375,0 3.15625,1.328125 1.23437,1.3125 1.23437,3.703125 0,0.15625 0,0.4375 h -7.21875 q 0.0937,1.59375 0.90625,2.453125 0.8125,0.84375 2.01563,0.84375 0.90625,0 1.54687,-0.46875 0.64063,
 -0.484375 1.01563,-1.515625 z m -5.39063,-2.65625 h 5.40625 q -0.10937,-1.21875 -0.625,-1.828125 -0.78125,-0.953125 -2.03125,-0.953125 -1.125,0 -1.90625,0.765625 -0.76562,0.75 -0.84375,2.015625 z"
+       id="path111"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path113"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,84.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path115"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path117"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,92.15187 h 12.664 v 7.173988 h -12.664 z"
+       id="path119"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path121"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,97.166214 h 15.94489 V 149.21876 H 796.6742 Z"
+       id="path123"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path125"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,97.166214 h 23.76062 v 28.860576 h -23.76062 z"
+       id="path127"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path129"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,132.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path131"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path133"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,132.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path135"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path137"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,113.40989 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path139"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path141"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,127.06765 0.47021,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18723 z"
+       id="path143"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path145"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,127.06765 0.47015,-0.4702 0.47022,0.4702 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18723 z"
+       id="path147"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,107.32317 h 14.62836 v 5.59806 H 850.9376 Z"
+       id="path149"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path151"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path153"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,149.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02659 0 c 0,-1.1702 0.85608,-2.11884 1.91211,-2.11884"
+       id="path155"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,125.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path157"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path159"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,115.21598 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path161"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path163"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,121.19038 h 7.92023 v 7.17399 h -7.92023 z"
+       id="path165"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path167"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,127.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path169"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path171"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,133.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path173"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path175"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,139.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path177"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path179"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,188.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path181"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path183"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,196.15187 h 12.664 v 7.17398 h -12.664 z"
+       id="path185"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path187"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,201.16621 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path189"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path191"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,201.16621 h 23.76062 v 28.86058 h -23.76062 z"
+       id="path193"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path195"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,236.55469 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path197"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path199"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,236.55469 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path201"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path203"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,217.40988 1.2937,-1.29367 v 0.64683 h 2.65796 v -0.64683 l 1.29364,1.29367 -1.29364,1.29367 v -0.64683 h -2.65796 v 0.64683 z"
+       id="path205"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path207"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,231.06764 0.47021,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47021,-0.4702 h 0.23511 v -4.18724 z"
+       id="path209"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path211"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,231.06764 0.47015,-0.47018 0.47022,0.47018 h -0.23511 v 4.18724 h 0.23511 l -0.47022,0.4702 -0.47015,-0.4702 h 0.23505 v -4.18724 z"
+       id="path213"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,211.32317 h 14.62836 v 5.59807 H 850.9376 Z"
+       id="path215"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884 z"
+       id="path217"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path219"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,253.05312 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02659 c 0,-1.1702 -0.85614,-2.11884 -1.91217,-2.11884 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11883 v -14.02658 0 c 0,-1.17021 0.85608,-2.11884 1.91211,-2.11884"
+       id="path221"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,229.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path223"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path225"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,219.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path227"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path229"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,225.19038 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path231"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path233"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,231.16478 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path235"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path237"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,237.13918 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path239"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path241"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,243.11357 h 7.92023 v 7.174 h -7.92023 z"
+       id="path243"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path245"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,284.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path247"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path249"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,292.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path251"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path253"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,297.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path255"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path257"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,297.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path259"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path261"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,332.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path263"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path265"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,332.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path267"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path269"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,313.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path271"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path273"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,327.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path275"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path277"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,327.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path279"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,307.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path281"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path283"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path285"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,349.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path287"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,325.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path289"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path291"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,315.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path293"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path295"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,321.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path297"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path299"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,327.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path301"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path303"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,333.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path305"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path307"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,339.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path309"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path311"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 784.2409,380.97754 h 86.01471 v 74.04492 H 784.2409 Z"
+       id="path313"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#cc0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path315"
+       inkscape:connector-curvature="0"
+       style="fill:#fdf8f8;fill-rule:evenodd" />
+    <path
+       d="m 854.0783,388.1519 h 12.664 v 7.17398 h -12.664 z"
+       id="path317"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path319"
+       inkscape:connector-curvature="0"
+       style="fill:#d9ead3;fill-rule:evenodd" />
+    <path
+       d="m 796.6742,393.1662 h 15.94489 v 52.05255 H 796.6742 Z"
+       id="path321"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path323"
+       inkscape:connector-curvature="0"
+       style="fill:#cfe2f3;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,393.1662 h 23.76062 v 28.8606 h -23.76062 z"
+       id="path325"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path327"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 817.86584,428.5547 h 10.95038 v 16.4884 h -10.95038 z"
+       id="path329"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path331"
+       inkscape:connector-curvature="0"
+       style="fill:#f4cccc;fill-rule:evenodd" />
+    <path
+       d="m 830.6742,428.5547 h 10.95032 v 16.4884 H 830.6742 Z"
+       id="path333"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path335"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 812.6282,409.40988 1.2937,-1.29367 v 0.64685 h 2.65796 v -0.64685 l 1.29364,1.29367 -1.29364,1.29367 v -0.64682 h -2.65796 v 0.64682 z"
+       id="path337"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path339"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 823.25336,423.06766 0.47021,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47021,-0.47021 h 0.23511 v -4.18723 z"
+       id="path341"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path343"
+       inkscape:connector-curvature="0"
+       style="fill:#eeeeee;fill-rule:evenodd" />
+    <path
+       d="m 835.02655,423.06766 0.47015,-0.47021 0.47022,0.47021 h -0.23511 v 4.18723 h 0.23511 l -0.47022,0.47021 -0.47015,-0.47021 h 0.23505 v -4.18723 z"
+       id="path345"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 850.9376,403.32318 h 14.62836 v 5.59805 H 850.9376 Z"
+       id="path347"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883 z"
+       id="path349"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path351"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 850.01074,445.0531 c -1.05603,0 -1.91211,-0.94864 -1.91211,-2.11884 v -14.02658 c 0,-1.17019 -0.85614,-2.11883 -1.91217,-2.11883 v 0 c 1.05603,0 1.91217,-0.94864 1.91217,-2.11884 v -14.02658 0 c 0,-1.17019 0.85608,-2.11883 1.91211,-2.11883"
+       id="path353"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 838.0996,421.12592 h 14.62836 v 5.59805 H 838.0996 Z"
+       id="path355"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path357"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,411.21597 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path359"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path361"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,417.19037 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path363"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path365"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,423.16476 h 7.92023 v 7.17401 h -7.92023 z"
+       id="path367"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path369"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,429.1392 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path371"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path373"
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-rule:evenodd" />
+    <path
+       d="m 855.78827,435.1136 h 7.92023 v 7.17398 h -7.92023 z"
+       id="path375"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#4a86e8;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90552,-71.85828 20.47638,-35.92914 40.95276,-71.85827 81.90552,-71.85827"
+       id="path377"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,261.25125 c 40.95276,0 61.42914,-35.92914 81.90546,-71.85826 10.23822,-17.96457 20.47638,-35.92915 33.27411,-49.40257 6.39886,-6.73671 13.43762,-12.35065 21.43622,-16.2804 3.99933,-1.96487 8.23858,-3.5087 12.75781,-4.56131 2.25958,-0.52631 4.58911,-0.92981 6.99371,-1.20174 1.20227,-0.13596 2.42334,-0.23902 3.66376,-0.3081 l 0.35388,-0.0172"
+       id="path379"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1456,117.62166 -1.0957,1.15275 3.06024,-1.20262 -3.11731,-1.04582 z"
+       id="path381"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74017,-33.62204 C 722.18577,242.81102 742.87083,226 784.24088,226"
+       id="path383"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,293.24408 c 41.37006,0 62.05511,-16.81101 82.74011,-33.62204 10.34253,-8.4055 20.68506,-16.81102 33.61322,-23.11514 6.46405,-3.15207 13.57452,-5.7788 21.6546,-7.61751 4.0401,-0.91934 8.32251,-1.64169 12.88776,-2.1342 2.28265,-0.24626 4.63592,-0.43506 7.06506,-0.5623 1.21448,-0.0636 2.448,-0.11184 3.70105,-0.14415 l 0.39166,-0.009"
+       id="path385"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 780.814,226.03992 -1.11139,1.1376 3.07648,-1.16049 -3.10266,-1.08851 z"
+       id="path387"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 19.86224,3.52755 39.72443,7.05511 79.44885,7.05511"
+       id="path389"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,333.24408 c 39.72443,0 59.58661,3.52759 79.44879,7.05515 9.93109,1.76376 19.86218,3.52755 32.27606,4.85037 6.20697,0.66144 13.03455,1.21261 20.79322,1.59842 3.87939,0.19293 7.99151,0.34451 12.37512,0.44784 2.19183,0.0517 4.45147,0.0913 6.78399,0.11798 1.1662,0.0133 2.35065,0.0235 3.55384,0.0303 l 0.2395,9.7e-4"
+       id="path391"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 774.23114,347.34512 -1.12762,1.12155 3.09283,-1.11627 -3.08673,-1.1329 z"
+       id="path393"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90552,22.1575 20.47638,11.07874 40.95276,22.15747 81.90552,22.15747"
+       id="path395"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 618.7606,397.24408 c 40.95276,0 61.42914,11.07877 81.90546,22.1575 10.23822,5.53937 20.47638,11.07874 33.27411,15.23328 6.39886,2.07724 13.43762,3.80829 21.43622,5.02005 3.99933,0.60583 8.23858,1.08188 12.75781,1.40649 2.25958,0.16226 4.58911,0.28668 6.99371,0.37052 1.20227,0.0419 2.42334,0.0737 3.66376,0.095 l 0.35291,0.005"
+       id="path397"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 3" />
+    <path
+       d="m 779.1446,441.53223 -1.1333,1.11575 3.09845,-1.10037 -3.08087,-1.14874 z"
+       id="path399"
+       inkscape:connector-curvature="0"
+       style="fill:#595959;fill-rule:evenodd;stroke:#595959;stroke-width:1;stroke-linecap:butt" />
+    <path
+       d="m 780.8373,58.973755 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path401"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="M 811.0304,80.77375 V 73.8675 h 1.0625 v 0.984375 q 0.75,-1.140625 2.1875,-1.140625 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.359375 0.375,0.859375 0.0625,0.328125 0.0625,1.140625 v 4.25 h -1.17188 v -4.203125 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.359375 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.796875 v 3.78125 z m 6.97498,-3.453125 q 0,-1.921875 1.07812,-2.84375 0.89063,-0.765625 2.17188,-0.765625 1.42187,0 2.32812,0.9375 0.90625,0.921875 0.90625,2.578125 0,1.328125 -0.40625,2.09375 -0.39062,0.765625 -1.15625,1.1875 -0.76562,0.421875 -1.67187,0.421875 -1.45313,0 -2.35938,-0.921875 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.328125 0.57813,1.984375 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.671875 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.984375 z m 
 11.13123,3.453125 v -0.875 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.453125 -0.6875,-0.453125 -1.07812,-1.265625 -0.375,-0.828125 -0.375,-1.890625 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.453125 1.54687,-0.453125 0.625,0 1.10938,0.265625 0.5,0.25 0.79687,0.671875 v -3.421875 h 1.17188 v 9.546875 z m -3.70313,-3.453125 q 0,1.328125 0.5625,1.984375 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.421875 -0.54687,-2.078125 Q 829.2616,74.68 828.46473,74.68 q -0.78125,0 -1.3125,0.640625 -0.51563,0.625 -0.51563,2 z m 11.3656,1.234375 1.20313,0.140625 q -0.28125,1.0625 -1.0625,1.65625 Q 837.3772,80.93 836.17408,80.93 q -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.609375 0,-1.75 0.89063,-2.703125 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.109375 0,0.3125 h -5.15625 q 0.0625,1.140625 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1
 .10937,-0.328125 0.45313,-0.34375 0.71875,-1.078125 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.859375 -0.4375,-1.296875 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.546875 -0.54688,0.53125 -0.60938,1.4375 z m 9.89667,-0.578125 q 0,-1.6875 0.34375,-2.71875 0.35938,-1.03125 1.04688,-1.59375 0.6875,-0.5625 1.71875,-0.5625 0.78125,0 1.35937,0.3125 0.57813,0.296875 0.95313,0.890625 0.375,0.578125 0.59375,1.421875 0.21875,0.828125 0.21875,2.25 0,1.671875 -0.35938,2.703125 -0.34375,1.03125 -1.03125,1.59375 -0.67187,0.5625 -1.73437,0.5625 -1.375,0 -2.15625,-0.984375 -0.95313,-1.1875 -0.95313,-3.875 z m 1.20313,0 q 0,2.34375 0.54687,3.125 0.5625,0.78125 1.35938,0.78125 0.8125,0 1.35937,-0.78125 0.5625,-0.78125 0.5625,-3.125 0,-2.359375 -0.5625,-3.125 -0.54687,-0.78125 -1.35937,-0.78125 -0.8125,0 -1.29688,0.6875 -0.60937,0.875 -0.60937,3.21875 z"
+       id="path403"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,162.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path405"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,184.77376 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 14.31855,4.125 h -1.17188 v -7.46875 q -0.42187,0.40625 -1.10937,0.8125 -0.6875,0.40625 -1.23438,0.60937 v -1.14062 q 0.98438,-0.45313 1.71875,-1.10938 0.73438,-0.67187 1.03125,-1.28125 h 0.76563 z"
+       id="path407"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,258.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path409"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,280.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 16.05292,3 v 1.125 h -6.29688 q -0.0156,-0.42188 0.14063,-0.8125 0.23437,-0.64063 0.76562,-1.26563 0.53125,-0.625 1.53125,-1.45312 1.5625,-1.26563 2.10938,-2.01563 0.54687,-0.75 0.54687,-1.40625 0,-0.70312 -0.5,-1.17187 -0.5,-0.48438 -1.29687,-0.48438 -0.85938,0 -1.375,0.51563 -0.5,0.5 -0.5,1.39062 l -1.20313,-0.10937 q 0.125,-1.35938 0.92188,-2.0625 0.8125,-0.70313 2.17187,-0.70313 1.375,0 2.17188,0.76563 0.8125,0.75 0.8125,1.875 0,0.57812 -0.23438,1.14062 -0.23437,0.54688 -0.78125,1.15625 -0.54687,0.60938 -1.8125,1.67188 -1.04687,0.89062 -1.35937,1.21875 -0.29688,0.3125 -0.48438,0.625 z"
+       id="path411"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+    <path
+       d="m 780.8373,354.97375 h 99.40155 v 27.46457 H 780.8373 Z"
+       id="path413"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:0;fill-rule:evenodd" />
+    <path
+       d="m 811.0304,376.77374 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z m 6.97498,-3.45313 q 0,-1.92187 1.07812,-2.84375 0.89063,-0.76562 2.17188,-0.76562 1.42187,0 2.32812,0.9375 0.90625,0.92187 0.90625,2.57812 0,1.32813 -0.40625,2.09375 -0.39062,0.76563 -1.15625,1.1875 -0.76562,0.42188 -1.67187,0.42188 -1.45313,0 -2.35938,-0.92188 -0.89062,-0.9375 -0.89062,-2.6875 z m 1.20312,0 q 0,1.32813 0.57813,1.98438 0.59375,0.65625 1.46875,0.65625 0.875,0 1.45312,-0.65625 0.57813,-0.67188 0.57813,-2.03125 0,-1.28125 -0.59375,-1.9375 -0.57813,-0.65625 -1.4375,-0.65625 -0.875,0 -1.46875,0.65625 -0.57813,0.65625 -0.57813,1.98437 z m 11.13123,3.45313 v -0.8
 75 q -0.65625,1.03125 -1.9375,1.03125 -0.8125,0 -1.51563,-0.45313 -0.6875,-0.45312 -1.07812,-1.26562 -0.375,-0.82813 -0.375,-1.89063 0,-1.03125 0.34375,-1.875 0.34375,-0.84375 1.03125,-1.28125 0.70312,-0.45312 1.54687,-0.45312 0.625,0 1.10938,0.26562 0.5,0.25 0.79687,0.67188 v -3.42188 h 1.17188 v 9.54688 z m -3.70313,-3.45313 q 0,1.32813 0.5625,1.98438 0.5625,0.65625 1.32813,0.65625 0.76562,0 1.29687,-0.625 0.53125,-0.625 0.53125,-1.90625 0,-1.42188 -0.54687,-2.07813 -0.54688,-0.67187 -1.34375,-0.67187 -0.78125,0 -1.3125,0.64062 -0.51563,0.625 -0.51563,2 z m 11.3656,1.23438 1.20313,0.14062 q -0.28125,1.0625 -1.0625,1.65625 -0.76563,0.57813 -1.96875,0.57813 -1.51563,0 -2.40625,-0.9375 -0.89063,-0.9375 -0.89063,-2.60938 0,-1.75 0.89063,-2.70312 0.90625,-0.96875 2.34375,-0.96875 1.39062,0 2.26562,0.9375 0.875,0.9375 0.875,2.65625 0,0.10937 0,0.3125 h -5.15625 q 0.0625,1.14062 0.64063,1.75 0.57812,0.59375 1.4375,0.59375 0.65625,0 1.10937,-0.32813 0.45313,-0.34375 0.71875,-1.078
 12 z m -3.84375,-1.90625 h 3.85938 q -0.0781,-0.85938 -0.4375,-1.29688 -0.5625,-0.6875 -1.45313,-0.6875 -0.8125,0 -1.35937,0.54688 -0.54688,0.53125 -0.60938,1.4375 z m 10.2248,4.125 v -6.90625 h 1.0625 v 0.98437 q 0.75,-1.14062 2.1875,-1.14062 0.625,0 1.15625,0.21875 0.53125,0.21875 0.78125,0.59375 0.26562,0.35937 0.375,0.85937 0.0625,0.32813 0.0625,1.14063 v 4.25 h -1.17188 v -4.20313 q 0,-0.71875 -0.14062,-1.0625 -0.14063,-0.35937 -0.48438,-0.5625 -0.34375,-0.21875 -0.8125,-0.21875 -0.75,0 -1.29687,0.46875 -0.54688,0.46875 -0.54688,1.79688 v 3.78125 z"
+       id="path415"
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-rule:nonzero" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/graph_mem_layout.svg b/doc/guides/prog_guide/img/graph_mem_layout.svg
new file mode 100644
index 000000000..1d41729c9
--- /dev/null
+++ b/doc/guides/prog_guide/img/graph_mem_layout.svg
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg428"
+   sodipodi:docname="Graph_mem_layout.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata434">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs432" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview430"
+     showgrid="false"
+     inkscape:zoom="3.4037037"
+     inkscape:cx="505.84248"
+     inkscape:cy="270.46053"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g426" />
+  <clipPath
+     id="g81c521b992_0_3.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path223" />
+  </clipPath>
+  <g
+     clip-path="url(#g81c521b992_0_3.0)"
+     id="g426">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path226" />
+    <path
+       fill="#ffffff"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path228" />
+    <path
+       stroke="#741b47"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m72.97638 40.356956l812.126 0l0 426.2362l-812.126 0z"
+       fill-rule="evenodd"
+       id="path230" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m143.0105 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path232" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m271.45407 100.022575l0 345.3753"
+       fill-rule="nonzero"
+       id="path234" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 100.52126l129.44095 0"
+       fill-rule="nonzero"
+       id="path236" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 151.71811l129.44095 0"
+       fill-rule="nonzero"
+       id="path238" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 204.91496l129.44095 0"
+       fill-rule="nonzero"
+       id="path240" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 240.11182l129.44095 0"
+       fill-rule="nonzero"
+       id="path242" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 291.30865l129.44095 0"
+       fill-rule="nonzero"
+       id="path244" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 342.50552l129.44095 0"
+       fill-rule="nonzero"
+       id="path246" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 393.70236l129.44095 0"
+       fill-rule="nonzero"
+       id="path248" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m142.51181 444.8992l129.44095 0"
+       fill-rule="nonzero"
+       id="path250" />
+    <path
+       fill="#000000"
+       d="m172.35416 118.58688l0 -1.125l4.03125 -0.015625l0 3.546875q-0.921875 0.75 -1.921875 1.125q-0.984375 0.359375 -2.03125 0.359375q-1.40625 0 -2.5625 -0.59375q-1.140625 -0.609375 -1.734375 -1.734375q-0.578125 -1.140625 -0.578125 -2.546875q0 -1.40625 0.578125 -2.609375q0.59375 -1.203125 1.6875 -1.78125q1.09375 -0.59375 2.515625 -0.59375q1.03125 0 1.859375 0.34375q0.84375 0.328125 1.3125 0.9375q0.484375 0.59375 0.734375 1.546875l-1.140625 0.3125q-0.21875 -0.71875 -0.53125 -1.140625q-0.3125 -0.421875 -0.90625 -0.671875q-0.59375 -0.25 -1.3125 -0.25q-0.875 0 -1.515625 0.265625q-0.625 0.265625 -1.015625 0.703125q-0.375 0.421875 -0.59375 0.9375q-0.359375 0.875 -0.359375 1.921875q0 1.265625 0.4375 2.125q0.4375 0.859375 1.265625 1.28125q0.84375 0.421875 1.796875 0.421875q0.8125 0 1.59375 -0.3125q0.78125 -0.328125 1.1875 -0.6875l0 -1.765625l-2.796875 0zm5.726425 3.734375l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.593
 75 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q
 -0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.7031
 25 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm11.302963 0l0 -9.546875l1.265625 0l0 3.921875l4.953125 0l0 -3.921875l1.265625 0l0 9.546875l-1.265625 0l0 -4.5l-4.953125 0l0 4.5l-1.265625 0zm14.172028 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.718
 75 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.3
 59375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm7.4749756 3.46875l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234
 375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path252" />
+    <path
+       fill="#000000"
+       d="m161.10791 135.96188l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.631226 -2.03125l1.1875 0.109375q0 0.40625 0.109375 0.609375q0.109375 0.203125 0.34375 0.3125q0.328125 0.140625
  0.828125 0.140625q1.0625 0 1.53125 -0.546875q0.3125 -0.375 0.578125 -1.625l0.109375 -0.5625q-0.921875 0.9375 -1.953125 0.9375q-1.046875 0 -1.75 -0.765625q-0.703125 -0.78125 -0.703125 -2.1875q0 -1.171875 0.546875 -2.140625q0.5625 -0.984375 1.328125 -1.46875q0.765625 -0.5 1.578125 -0.5q1.359375 0 2.09375 1.28125l0.234375 -1.125l1.078125 0l-1.390625 6.671875q-0.21875 1.09375 -0.59375 1.703125q-0.375 0.625 -1.03125 0.953125q-0.65625 0.34375 -1.53125 0.34375q-0.828125 0 -1.4375 -0.21875q-0.59375 -0.203125 -0.890625 -0.625q-0.296875 -0.40625 -0.296875 -0.953125q0 -0.15625 0.03125 -0.34375zm1.46875 -3.6875q0 0.71875 0.140625 1.078125q0.203125 0.5 0.5625 0.765625q0.359375 0.25 0.796875 0.25q0.578125 0 1.140625 -0.40625q0.578125 -0.40625 0.9375 -1.25q0.359375 -0.859375 0.359375 -1.625q0 -0.859375 -0.484375 -1.359375q-0.46875 -0.515625 -1.15625 -0.515625q-0.4375 0 -0.84375 0.234375q-0.390625 0.234375 -0.75 0.703125q-0.34375 0.46875 -0.53125 1.140625q-0.171875 0.65625 -0.171875 0.9843
 75zm6.0062256 3.0625l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.110245 -0.859375q-0.625 0.53125 -1.1875 0.78125q-0.5625 0.234375 -1.203125 0.234375q-0.96875 0 -1.5625 -0.5625q-0.578125 -0.5625 -0.578125 -1.4375q0 -0.578125 0.265625 -1.015625q0.265625 -0.453125 0.703125 -0.71875q0.4375 -0.28125 1.0625 -0.390625q0.40625 -0.078125 1.515625 -0.125q1.109375 -0.046875 1.59375 -0.234375q0.125 -0.484375 0.125 -0.8125q0 -0.40625 -0.296875 -0.640625q-0.40625 -0.328125 -1.1875 -0.328125q-0.75 0 -1.21875 0.328125q-0.46875 0.328125 -0.6875 0.9375l-1.1875 -0.109375q0.359375 -1.015625 1.140625 -1.5625q0.796875 -0.546875 2.0 -0.546875q1.28125 0 2.03125 0.609375q0.578125 0.453125 0.578125 1.1875q0 0.546875 -0.15625 1.28125l-0.390625 1.71875q-0.1875 0.8
 125 -0.1875 1.328125q0 0.328125 0.15625 0.9375l-1.203125 0q-0.09375 -0.34375 -0.125 -0.859375zm0.421875 -2.640625q-0.234375 0.09375 -0.53125 0.15625q-0.28125 0.046875 -0.9375 0.109375q-1.03125 0.078125 -1.453125 0.21875q-0.421875 0.140625 -0.640625 0.453125q-0.21875 0.296875 -0.21875 0.671875q0 0.5 0.34375 0.828125q0.34375 0.3125 0.984375 0.3125q0.578125 0 1.109375 -0.3125q0.546875 -0.3125 0.859375 -0.859375q0.3125 -0.5625 0.484375 -1.578125zm1.7406006 6.15625l2.0 -9.5625l1.09375 0l-0.203125 0.953125q0.59375 -0.625 1.078125 -0.859375q0.484375 -0.25 1.015625 -0.25q0.984375 0 1.625 0.71875q0.65625 0.71875 0.65625 2.0625q0 1.078125 -0.359375 1.96875q-0.34375 0.875 -0.875 1.421875q-0.515625 0.546875 -1.046875 0.796875q-0.53125 0.25 -1.09375 0.25q-1.25 0 -1.921875 -1.265625l-0.78125 3.765625l-1.1875 0zm2.328125 -5.484375q0 0.78125 0.109375 1.078125q0.171875 0.421875 0.53125 0.6875q0.375 0.25 0.875 0.25q1.015625 0 1.640625 -1.140625q0.625 -1.140625 0.625 -2.328125q0 -0.875 -0.4218
 75 -1.359375q-0.421875 -0.484375 -1.046875 -0.484375q-0.453125 0 -0.84375 0.25q-0.375 0.234375 -0.703125 0.703125q-0.328125 0.46875 -0.546875 1.15625q-0.21875 0.6875 -0.21875 1.1875zm5.6624756 2.828125l2.0 -9.546875l1.171875 0l-0.765625 3.671875q0.65625 -0.640625 1.21875 -0.90625q0.578125 -0.28125 1.171875 -0.28125q0.859375 0 1.328125 0.453125q0.484375 0.453125 0.484375 1.1875q0 0.359375 -0.203125 1.34375l-0.859375 4.078125l-1.171875 0l0.875 -4.1875q0.1875 -0.90625 0.1875 -1.140625q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.671875 -0.21875q-0.640625 0 -1.21875 0.34375q-0.578125 0.328125 -0.90625 0.90625q-0.328125 0.578125 -0.609375 1.875l-0.609375 2.96875l-1.1875 0z"
+       fill-rule="nonzero"
+       id="path254" />
+    <path
+       fill="#000000"
+       d="m189.80461 173.51811l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875
  -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.968
 75 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path256" />
+    <path
+       fill="#000000"
+       d="m160.76096 182.86186q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125
  -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.93988 -1.703125q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0625 0.046875 -0.125 0.046875l-2.359375 0l-1.46875 7.296875q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.26
 5625 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 -0.015625 -0.109375l1.46875 -7.296875l-2.359375 0q-0.09375 0 -0.125 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.09375 0.046875 -0.171875q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.078125 0.09375 -0.109375q0.046875 -0.046875 0.109375 -0.046875l5.859375 0q0.078125 0 0.109375 0.0625q0.03125 0.0625 0.03125 0.171875zm5.838608 -0.015625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.0468
 75 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm5.4453735 9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625
  -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm9.278656 -9.234375q0 0.046875 -0.015625 0.109375q-0.015625 0.0625 -0.03125 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.078125 0.109375q-0.03125 0.03125 -0.078125 0.03125q-0.09375 0 -0.265625 -0.109375q-0.171875 -0.125 -0.453125 -0.265625q-0.265625 -0.15625 -0.671875 -0.265625q-0.390625 -0.125 -0.9375 -0.125q-0.578125 0 -1.09375 0.171875q-0.5 0.171875 -0.921875 0.484375q-0.421875 0.296875 -0.75 0.703125q-0.328125 0.40625 -0.5625 0.90625q-0.234375 0.484375 -0.359375 1.015625q-0.109375 0.53125 -0.109375 1.078125q0 0.5625 0.15625 1.015625q0.171875 0.4375 0.484375 0.734375q0.3125 0.296875 0.75 0.453125q0.4375 0.15625 0.984375 0.15625q0.40625 0 0.84375 -0.09375q0.453125 -0.09375 0.828125 -0.296875l0.484375 -2.4375l-1.953125 0q-0.078125 0 -0.125 -0.046875q-0.03125 -0.0625 -
 0.03125 -0.171875q0 -0.046875 0 -0.109375q0.015625 -0.078125 0.03125 -0.15625q0.015625 -0.078125 0.046875 -0.15625q0.03125 -0.078125 0.0625 -0.140625q0.046875 -0.0625 0.09375 -0.09375q0.046875 -0.046875 0.109375 -0.046875l2.671875 0q0.1875 0 0.265625 0.125q0.078125 0.125 0.03125 0.328125l-0.640625 3.25q-0.03125 0.125 -0.078125 0.203125q-0.03125 0.0625 -0.09375 0.125q-0.046875 0.0625 -0.296875 0.171875q-0.234375 0.109375 -0.59375 0.234375q-0.359375 0.109375 -0.8125 0.1875q-0.453125 0.09375 -0.953125 0.09375q-0.84375 0 -1.484375 -0.203125q-0.640625 -0.21875 -1.078125 -0.640625q-0.4375 -0.421875 -0.671875 -1.015625q-0.21875 -0.59375 -0.21875 -1.328125q0 -0.703125 0.15625 -1.375q0.15625 -0.671875 0.453125 -1.28125q0.3125 -0.609375 0.75 -1.109375q0.4375 -0.515625 1.0 -0.890625q0.578125 -0.375 1.25 -0.578125q0.6875 -0.21875 1.484375 -0.21875q0.453125 0 0.84375 0.078125q0.40625 0.078125 0.71875 0.203125q0.3125 0.109375 0.515625 0.25q0.21875 0.125 0.296875 0.203125q0.078125 0.0625 0
 .109375 0.140625q0.03125 0.0625 0.03125 0.15625zm6.9862976 0.84375q0 0.453125 -0.125 0.859375q-0.125 0.390625 -0.390625 0.734375q-0.25 0.34375 -0.640625 0.609375q-0.375 0.25 -0.890625 0.421875q0.34375 0.125 0.546875 0.515625q0.21875 0.390625 0.34375 1.046875l0.34375 1.859375q0.015625 0.125 0.03125 0.234375q0.015625 0.109375 0.015625 0.1875q0 0.0625 -0.03125 0.109375q-0.03125 0.046875 -0.109375 0.078125q-0.0625 0.015625 -0.1875 0.015625q-0.125 0.015625 -0.3125 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 0 -0.15625 -0.03125q-0.046875 -0.03125 -0.078125 -0.078125q-0.015625 -0.0625 -0.03125 -0.140625l-0.328125 -1.984375q-0.0625 -0.34375 -0.15625 -0.625q-0.09375 -0.28125 -0.265625 -0.484375q-0.15625 -0.203125 -0.421875 -0.3125q-0.265625 -0.109375 -0.640625 -0.109375l-0.75 0l-0.71875 3.59375q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.15625 0 -0.265625 -0.015625q-0.09375 0
  -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.5625 -7.8125q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.34375 -0.109375l1.828125 0q0.609375 0 1.0625 0.125q0.453125 0.109375 0.75 0.34375q0.3125 0.21875 0.453125 0.546875q0.15625 0.328125 0.15625 0.75zm-1.203125 0.171875q0 -0.21875 -0.078125 -0.40625q-0.078125 -0.1875 -0.25 -0.328125q-0.15625 -0.140625 -0.4375 -0.203125q-0.265625 -0.078125 -0.640625 -0.078125l-1.15625 0l-0.578125 2.84375l0.984375 0q0.578125 0 0.984375 -0.15625q0.421875 -0.15625 0.671875 -0.40625q0.25 -0.265625 0.375 -0.59375q0.125 -0.328125 0.125 -0.671875zm8.424255 6.09375q0.03125 0.140625 0.015625 0.234375q-0.015625 0.078125 -0.078125 0.125q-0.046875 0.046875 -0.1875 0.046875q-0.125 0.015625 -0.34375 0.015625q-0.140625 0 -0.25 -0.015625q-0.109375 0 -0.171875 -0.015625q-0.046875 -0.03125 -0.078125 -0.0625q-0.015625 -0.046875 -0.03125 -0.09375l-0.3125 -2.0625l-3.5 0l-1.09375 2.03125q-0.046875 0.078125 -0.09375 0.
 125q-0.03125 0.03125 -0.109375 0.0625q-0.078125 0.015625 -0.203125 0.015625q-0.109375 0.015625 -0.28125 0.015625q-0.203125 0 -0.3125 -0.015625q-0.125 -0.015625 -0.15625 -0.046875q-0.046875 -0.046875 -0.03125 -0.140625q0.015625 -0.09375 0.09375 -0.234375l4.375 -7.8125q0.046875 -0.078125 0.09375 -0.125q0.0625 -0.046875 0.140625 -0.0625q0.09375 -0.03125 0.21875 -0.03125q0.125 -0.015625 0.3125 -0.015625q0.21875 0 0.34375 0.015625q0.140625 0 0.21875 0.03125q0.09375 0.015625 0.125 0.0625q0.03125 0.046875 0.046875 0.125l1.25 7.828125zm-2.1875 -6.90625l0 0l-2.28125 4.1875l2.921875 0l-0.640625 -4.1875zm9.930588 0.859375q0 0.34375 -0.078125 0.71875q-0.078125 0.375 -0.265625 0.734375q-0.171875 0.359375 -0.4375 0.6875q-0.265625 0.328125 -0.65625 0.578125q-0.375 0.234375 -0.875 0.375q-0.5 0.140625 -1.1875 0.140625l-1.15625 0l-0.59375 3.046875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-
 0.15625 0 -0.265625 -0.015625q-0.09375 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.09375 -0.0625q-0.015625 -0.046875 0 -0.09375l1.546875 -7.78125q0.046875 -0.265625 0.203125 -0.375q0.171875 -0.109375 0.390625 -0.109375l1.65625 0q0.328125 0 0.578125 0.03125q0.25 0.015625 0.484375 0.0625q0.359375 0.078125 0.640625 0.25q0.28125 0.15625 0.46875 0.40625q0.203125 0.234375 0.296875 0.546875q0.109375 0.3125 0.109375 0.6875zm-1.1875 0.109375q0 -0.40625 -0.203125 -0.6875q-0.1875 -0.296875 -0.609375 -0.40625q-0.15625 -0.046875 -0.34375 -0.0625q-0.1875 -0.015625 -0.40625 -0.015625l-1.046875 0l-0.671875 3.375l1.0625 0q0.46875 0 0.78125 -0.09375q0.328125 -0.109375 0.5625 -0.28125q0.25 -0.171875 0.40625 -0.390625q0.171875 -0.234375 0.265625 -0.46875q0.109375 -0.25 0.15625 -0.5q0.046875 -0.25 0.046875 -0.46875zm7.76033 6.171875q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0
 .015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.0625 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l0.734375 -3.75l-3.828125 0l-0.734375 3.75q-0.015625 0.046875 -0.046875 0.09375q-0.03125 0.03125 -0.109375 0.046875q-0.078125 0.015625 -0.1875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.171875 0 -0.28125 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.09375l1.609375 -8.109375q0.015625 -0.03125 0.046875 -0.0625q0.046875 -0.046875 0.109375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.265625 0.015625q0.109375 0.015625 0.15625 0.046875q0.0625 0.015625 0.078125 0.0625q0.015625 0.03125 0.015625 0.0625l-0.671875 3.390625l3.828125 0l0.671875 -3.390625q0.015625 -0.03125 0.046875 -0.0625q0.03125 -0.046875 0.09375 -0.0625q0.078125 -0.03125 0.1875 -0.046875q0.109375 -0.015625 0.28125 -0.015625q0.15625 0 0.25 0.015625q0.109375 0.015625 0.171875 0.046875q0.0625 
 0.015625 0.078125 0.0625q0.015625 0.03125 0 0.0625l-1.609375 8.109375zm7.3821716 1.890625q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.125q-0.015625 0.0625 -0.046875 0.140625q-0.03125 0.078125 -0.078125 0.125q-0.03125 0.0625 -0.078125 0.09375q-0.046875 0.046875 -0.109375 0.046875l-6.265625 0q-0.078125 0 -0.109375 -0.0625q-0.03125 -0.0625 -0.03125 -0.15625q0 -0.03125 0 -0.09375q0 -0.0625 0.015625 -0.140625q0.015625 -0.0625 0.046875 -0.140625q0.015625 -0.0625 0.0625 -0.140625q0.03125 -0.0625 0.078125 -0.09375q0.046875 -0.03125 0.109375 -0.03125l6.265625 0q0.078125 0 0.109375 0.0625q0.046875 0.0625 0.046875 0.15625zm7.497406 -9.9375q0 0.046875 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.171875q-0.03125 0.078125 -0.078125 0.15625q-0.03125 0.0625 -0.078125 0.109375q-0.046875 0.046875 -0.125 0.046875l-3.078125 0l-0.5625 2.859375l2.90625 0q0.078125 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.140625q0 0.0625 -0.015625 0.140625q0 0.0625 -0.015625 0.140625q-
 0.015625 0.0625 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.90625 0l-0.703125 3.5q-0.015625 0.0625 -0.046875 0.109375q-0.03125 0.03125 -0.109375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.28125 0.015625q-0.15625 0 -0.265625 -0.015625q-0.109375 -0.015625 -0.171875 -0.03125q-0.046875 -0.015625 -0.078125 -0.046875q-0.015625 -0.046875 0 -0.109375l1.5625 -7.796875q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l3.78125 0q0.09375 0 0.125 0.0625q0.03125 0.0625 0.03125 0.15625zm6.3270416 0q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.078125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.07812
 5 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.609375 3.0l3.34375 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.046875 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.5 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875zm6.7109985 7.734375q-0.015625 0.140625 -0.078125 0.234375q-0.0625 0.078125 -0.140625 0.140625q-0.0625 0.0625 -0.15625 0.09375q-0.078125 0.015625 -0.171875 0.015625l-0.421875 0q-0.171875 0 -0.296875 -0.03125q-0.109375 -0.046875 -0.203125 -0.140625q-0.078125 -0.09375 -0.15625 -0.234375q-0.0625 -0
 .15625 -0.140625 -0.390625l-1.578125 -4.484375q-0.15625 -0.484375 -0.328125 -0.953125q-0.15625 -0.484375 -0.296875 -0.96875l-0.015625 0q-0.078125 0.53125 -0.1875 1.0625q-0.09375 0.515625 -0.203125 1.046875l-0.96875 4.90625q-0.015625 0.0625 -0.0625 0.109375q-0.03125 0.03125 -0.09375 0.046875q-0.0625 0.015625 -0.171875 0.03125q-0.109375 0.015625 -0.265625 0.015625q-0.140625 0 -0.25 -0.015625q-0.09375 -0.015625 -0.15625 -0.03125q-0.046875 -0.015625 -0.0625 -0.046875q-0.015625 -0.046875 0 -0.109375l1.546875 -7.765625q0.046875 -0.265625 0.21875 -0.375q0.171875 -0.109375 0.34375 -0.109375l0.5 0q0.15625 0 0.265625 0.03125q0.125 0.03125 0.203125 0.125q0.09375 0.078125 0.15625 0.21875q0.078125 0.125 0.15625 0.328125l1.59375 4.5625q0.140625 0.421875 0.28125 0.84375q0.15625 0.421875 0.296875 0.84375l0.015625 0q0.09375 -0.53125 0.203125 -1.09375q0.109375 -0.578125 0.203125 -1.109375l0.921875 -4.5625q0.015625 -0.0625 0.046875 -0.09375q0.03125 -0.03125 0.09375 -0.0625q0.0625 -0.03125 0.15
 625 -0.03125q0.109375 -0.015625 0.265625 -0.015625q0.15625 0 0.25 0.015625q0.109375 0 0.15625 0.03125q0.0625 0.03125 0.078125 0.0625q0.015625 0.03125 0.015625 0.09375l-1.5625 7.765625zm9.090393 -7.09375q0 0.140625 -0.046875 0.34375q-0.046875 0.203125 -0.125 0.328125q-0.078125 0.109375 -0.15625 0.109375q-0.09375 0 -0.21875 -0.125q-0.125 -0.125 -0.328125 -0.265625q-0.203125 -0.140625 -0.53125 -0.25q-0.328125 -0.125 -0.828125 -0.125q-0.546875 0 -1.0 0.203125q-0.4375 0.203125 -0.8125 0.5625q-0.359375 0.34375 -0.640625 0.796875q-0.265625 0.453125 -0.453125 0.953125q-0.171875 0.5 -0.265625 1.015625q-0.078125 0.5 -0.078125 0.953125q0 0.515625 0.125 0.921875q0.125 0.40625 0.375 0.6875q0.25 0.28125 0.609375 0.421875q0.359375 0.140625 0.8125 0.140625q0.515625 0 0.875 -0.109375q0.375 -0.109375 0.625 -0.25q0.265625 -0.140625 0.4375 -0.25q0.1875 -0.125 0.296875 -0.125q0.078125 0 0.109375 0.0625q0.03125 0.046875 0.03125 0.15625q0 0.03125 -0.015625 0.09375q-0.015625 0.0625 -0.03125 0.14062
 5q0 0.0625 -0.015625 0.15625q-0.015625 0.078125 -0.046875 0.15625q-0.03125 0.0625 -0.0625 0.125q-0.015625 0.0625 -0.09375 0.125q-0.0625 0.0625 -0.28125 0.203125q-0.21875 0.125 -0.53125 0.234375q-0.3125 0.109375 -0.71875 0.1875q-0.390625 0.09375 -0.828125 0.09375q-0.671875 0 -1.203125 -0.203125q-0.53125 -0.203125 -0.90625 -0.578125q-0.375 -0.390625 -0.578125 -0.96875q-0.203125 -0.578125 -0.203125 -1.328125q0 -0.609375 0.125 -1.265625q0.140625 -0.65625 0.390625 -1.265625q0.25 -0.625 0.625 -1.171875q0.390625 -0.546875 0.890625 -0.953125q0.5 -0.421875 1.125 -0.65625q0.640625 -0.25 1.390625 -0.25q0.484375 0 0.890625 0.109375q0.421875 0.109375 0.703125 0.28125q0.296875 0.15625 0.421875 0.28125q0.140625 0.125 0.140625 0.296875zm6.2602844 -0.640625q0 0.03125 0 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-3.296875 0l-0.53125 2.640625l2.828125 0q0.07
 8125 0 0.109375 0.046875q0.046875 0.046875 0.046875 0.15625q0 0.03125 -0.015625 0.109375q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.046875 0.140625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.046875 -0.109375 0.046875l-2.828125 0l-0.60935974 3.0l3.3437347 0q0.0625 0 0.109375 0.0625q0.046875 0.046875 0.046875 0.15625q0 0.046875 -0.015625 0.125q0 0.0625 -0.015625 0.140625q-0.015625 0.078125 -0.046875 0.15625q-0.015625 0.078125 -0.0625 0.15625q-0.03125 0.0625 -0.09375 0.109375q-0.046875 0.03125 -0.109375 0.03125l-4.0468597 0q-0.078125 0 -0.15625 -0.015625q-0.0625 -0.03125 -0.109375 -0.078125q-0.046875 -0.0625 -0.0625 -0.140625q-0.015625 -0.09375 0.015625 -0.21875l1.4999847 -7.515625q0.046875 -0.25 0.203125 -0.34375q0.15625 -0.109375 0.296875 -0.109375l4.0 0q0.140625 0 0.140625 0.21875z"
+       fill-rule="nonzero"
+       id="path258" />
+    <path
+       fill="#000000"
+       d="m172.59329 223.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859
 375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.250732 0l0 -9.546875l3.59375 0q1.09375 0 1.75 0.296875q0.65625 0.28125 1.03125 0.890625q0.375 0.609375 0.375 1.265625q0 0.609375 -0.34375 1.15625q-0.328125 0.53125 -0.984375 0.859375q0.859375 0.25 1.328125 0.875q0.46875 0.609375 0.
 46875 1.4375q0 0.671875 -0.296875 1.25q-0.28125 0.578125 -0.703125 0.890625q-0.40625 0.3125 -1.03125 0.46875q-0.625 0.15625 -1.546875 0.15625l-3.640625 0zm1.265625 -5.53125l2.0625 0q0.84375 0 1.203125 -0.109375q0.484375 -0.140625 0.71875 -0.46875q0.25 -0.34375 0.25 -0.84375q0 -0.46875 -0.234375 -0.828125q-0.21875 -0.359375 -0.640625 -0.5q-0.421875 -0.140625 -1.453125 -0.140625l-1.90625 0l0 2.890625zm0 4.40625l2.375 0q0.609375 0 0.859375 -0.046875q0.4375 -0.078125 0.734375 -0.25q0.296875 -0.1875 0.484375 -0.53125q0.1875 -0.359375 0.1875 -0.8125q0 -0.53125 -0.28125 -0.921875q-0.265625 -0.40625 -0.75 -0.5625q-0.484375 -0.15625 -1.40625 -0.15625l-2.203125 0l0 3.28125zm12.06163 1.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 
 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm3.1624756 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm8.156113 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625
  0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5062256 4.125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path260" />
+    <path
+       fill="#000000"
+       d="m186.7589 261.9118l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896713 -0.5781
 25q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875z"
+       fill-rule="nonzero"
+       id="path262" />
+    <path
+       fill="#000000"
+       d="m163.32709 275.55243l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path264" />
+    <path
+       fill="#000000"
+       d="m186.7589 313.10867l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 
 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318588 4.125
 l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125z"
+       fill-rule="nonzero"
+       id="path266" />
+    <path
+       fill="#000000"
+       d="m163.32709 326.7493l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.42187
 5 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125 
 0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -
 1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125
 q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125 
 1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.0156
 25 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-
 0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path268" />
+    <path
+       fill="#000000"
+       d="m186.7589 364.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0
  1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052963 3.0l0 
 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0z"
+       fill-rule="nonzero"
+       id="path270" />
+    <path
+       fill="#000000"
+       d="m163.32709 377.94614l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path272" />
+    <path
+       fill="#000000"
+       d="m185.65256 415.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625
  0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365463 4.12
 5l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0z"
+       fill-rule="nonzero"
+       id="path274" />
+    <path
+       fill="#000000"
+       d="m163.32709 429.14297l1.1875 -0.078125q0 0.515625 0.15625 0.875q0.15625 0.359375 0.578125 0.59375q0.421875 0.21875 0.96875 0.21875q0.78125 0 1.171875 -0.3125q0.390625 -0.3125 0.390625 -0.734375q0 -0.3125 -0.234375 -0.578125q-0.234375 -0.28125 -1.171875 -0.671875q-0.9375 -0.40625 -1.1875 -0.578125q-0.4375 -0.265625 -0.671875 -0.625q-0.21875 -0.359375 -0.21875 -0.828125q0 -0.8125 0.65625 -1.390625q0.65625 -0.59375 1.828125 -0.59375q1.296875 0 1.96875 0.609375q0.6875 0.59375 0.71875 1.578125l-1.15625 0.078125q-0.03125 -0.625 -0.453125 -0.984375q-0.40625 -0.375 -1.171875 -0.375q-0.609375 0 -0.953125 0.28125q-0.328125 0.28125 -0.328125 0.609375q0 0.3125 0.296875 0.5625q0.1875 0.171875 1.0 0.53125q1.359375 0.578125 1.703125 0.921875q0.5625 0.53125 0.5625 1.3125q0 0.515625 -0.3125 1.015625q-0.3125 0.484375 -0.96875 0.78125q-0.640625 0.296875 -1.515625 0.296875q-1.203125 0 -2.046875 -0.59375q-0.84375 -0.59375 -0.796875 -1.921875zm9.3203125 1.40625l-0.203125 0.953125q-0.4218
 75 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm0.9373627 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm9.15712 -1.25q-1.234375 1.40625 -2.546875 1.40625q-0.796875 0 -1.296875 -0.453125q-0.484375 -0.46875 -0.484375 -1.125q0 -0.4375 0.21875 -1.5l0.84375 -3.984375l1.171875 0l-0.921875 4.40625q-0.109375 0.5625 -0.109375 0.859375q0 0.390625 0.234375 0.609375q0.234375 0.21875 0.703125
  0.21875q0.484375 0 0.953125 -0.234375q0.484375 -0.234375 0.8125 -0.640625q0.34375 -0.421875 0.5625 -0.984375q0.140625 -0.359375 0.328125 -1.25l0.625 -2.984375l1.1875 0l-1.453125 6.90625l-1.078125 0l0.25 -1.25zm7.4749756 -1.265625l1.171875 0.125q-0.4375 1.296875 -1.265625 1.921875q-0.8125 0.625 -1.84375 0.625q-1.140625 0 -1.84375 -0.71875q-0.6875 -0.734375 -0.6875 -2.046875q0 -1.125 0.4375 -2.21875q0.453125 -1.09375 1.28125 -1.65625q0.84375 -0.578125 1.921875 -0.578125q1.109375 0 1.765625 0.625q0.65625 0.625 0.65625 1.65625l-1.15625 0.078125q-0.015625 -0.65625 -0.390625 -1.015625q-0.375 -0.375 -0.984375 -0.375q-0.703125 0 -1.234375 0.453125q-0.515625 0.4375 -0.8125 1.359375q-0.296875 0.90625 -0.296875 1.75q0 0.890625 0.390625 1.34375q0.390625 0.4375 0.96875 0.4375q0.5625 0 1.078125 -0.4375q0.53125 -0.4375 0.84375 -1.328125zm4.6484375 1.5625l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 
 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.328125q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm4.6403503 0.953125l1.453125 -6.90625l1.03125 0l-0.28125 1.40625q0.53125 -0.796875 1.03125 -1.171875q0.515625 -0.390625 1.046875 -0.390625q0.359375 0 0.875 0.25l-0.484375 1.09375q-0.3125 -0.21875 -0.671875 -0.21875q-0.625 0 -1.28125 0.6875q-0.640625 0.6875 -1.015625 2.484375l-0.578125 2.765625l-1.125 0zm7.2039948 -0.953125l-0.203125 0.953125q-0.421875 0.109375 -0.8125 0.109375q-0.703125 0 -1.125 -0.34375q-0.3125 -0.25 -0.3125 -0.703125q0 -0.234375 0.171875 -1.046875l0.828125 -4.015625l-0.921875 0l0.1875 -0.90625l0.9375 0l0.34375 -1.703125l1.359375 -0.8125l-0.53125 2.515625l1.15625 0l-0.1875 0.90625l-1.15625 0l-0.796875 3.8125q-0.15625 0.734375 -0.15625 0.875q0 0.21875 0.109375 0.32812
 5q0.125 0.109375 0.40625 0.109375q0.390625 0 0.703125 -0.078125zm6.0154877 -1.390625l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q-0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625zm4.4749756 6.71875l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.693726 -2.65625l1.453125 -6.90625l1.0625 0l-0.25 1.203125q0.6875 -0.71875 1.296875 -1.03125q0.609375 -0.328125
  1.234375 -0.328125q0.84375 0 1.3125 0.453125q0.484375 0.453125 0.484375 1.21875q0 0.375 -0.171875 1.203125l-0.875 4.1875l-1.171875 0l0.921875 -4.375q0.125 -0.640625 0.125 -0.953125q0 -0.34375 -0.234375 -0.546875q-0.234375 -0.21875 -0.6875 -0.21875q-0.90625 0 -1.609375 0.65625q-0.703125 0.640625 -1.03125 2.21875l-0.671875 3.21875l-1.1875 0zm7.6312256 -2.625q0 -2.015625 1.1875 -3.34375q0.984375 -1.09375 2.578125 -1.09375q1.25 0 2.015625 0.78125q0.765625 0.78125 0.765625 2.109375q0 1.1875 -0.484375 2.21875q-0.484375 1.015625 -1.375 1.5625q-0.890625 0.546875 -1.875 0.546875q-0.796875 0 -1.46875 -0.34375q-0.65625 -0.34375 -1.0 -0.96875q-0.34375 -0.640625 -0.34375 -1.46875zm1.171875 -0.109375q0 0.96875 0.46875 1.484375q0.46875 0.5 1.1875 0.5q0.375 0 0.75 -0.15625q0.375 -0.15625 0.6875 -0.46875q0.328125 -0.3125 0.546875 -0.703125q0.21875 -0.40625 0.359375 -0.875q0.203125 -0.640625 0.203125 -1.234375q0 -0.9375 -0.46875 -1.453125q-0.46875 -0.515625 -1.1875 -0.515625q-0.5625 0 -1.015
 625 0.265625q-0.453125 0.265625 -0.828125 0.78125q-0.359375 0.5 -0.53125 1.171875q-0.171875 0.671875 -0.171875 1.203125zm10.693726 1.734375q-1.015625 1.15625 -2.109375 1.15625q-0.984375 0 -1.640625 -0.71875q-0.65625 -0.734375 -0.65625 -2.109375q0 -1.265625 0.515625 -2.3125q0.515625 -1.046875 1.296875 -1.5625q0.78125 -0.515625 1.5625 -0.515625q1.28125 0 1.9375 1.234375l0.78125 -3.71875l1.171875 0l-1.984375 9.546875l-1.09375 0l0.21875 -1.0zm-3.234375 -1.890625q0 0.71875 0.140625 1.140625q0.140625 0.40625 0.484375 0.6875q0.34375 0.28125 0.828125 0.28125q0.796875 0 1.453125 -0.84375q0.875 -1.109375 0.875 -2.734375q0 -0.8125 -0.4375 -1.265625q-0.421875 -0.46875 -1.078125 -0.46875q-0.421875 0 -0.765625 0.1875q-0.34375 0.1875 -0.6875 0.640625q-0.34375 0.453125 -0.578125 1.15625q-0.234375 0.6875 -0.234375 1.21875zm11.053101 0.546875l1.15625 0.109375q-0.25 0.859375 -1.140625 1.625q-0.890625 0.765625 -2.125 0.765625q-0.765625 0 -1.40625 -0.34375q-0.640625 -0.359375 -0.984375 -1.03125q
 -0.328125 -0.6875 -0.328125 -1.546875q0 -1.140625 0.515625 -2.203125q0.53125 -1.0625 1.359375 -1.578125q0.84375 -0.515625 1.8125 -0.515625q1.234375 0 1.96875 0.765625q0.75 0.765625 0.75 2.09375q0 0.5 -0.09375 1.046875l-5.09375 0q-0.03125 0.1875 -0.03125 0.359375q0 0.96875 0.4375 1.484375q0.453125 0.5 1.109375 0.5q0.59375 0 1.171875 -0.390625q0.59375 -0.390625 0.921875 -1.140625zm-3.421875 -1.71875l3.875 0q0.015625 -0.1875 0.015625 -0.265625q0 -0.875 -0.453125 -1.34375q-0.4375 -0.484375 -1.125 -0.484375q-0.765625 0 -1.390625 0.53125q-0.609375 0.515625 -0.921875 1.5625z"
+       fill-rule="nonzero"
+       id="path276" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m92.06693 62.29134l230.33072 0l0 27.464565l-230.33072 0z"
+       fill-rule="evenodd"
+       id="path278" />
+    <path
+       fill="#000000"
+       d="m114.3782 84.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0164948 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.8748627 -1.171875l1.2031174 0.140625q-0.28125 1.0625 -1.0624924 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375
  -0.96875q1.390625 0 2.265625 0.9375q0.8749924 0.9375 0.8749924 2.65625q0 0.109375 0 0.3125l-5.1562424 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.443718 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.271851 -2.078125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125 2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.
 390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm6.6312256 3.578125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.96962 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -
 0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9906006 6.125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.45
 3125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm6.3499756 3.421875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm10.677963 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -
 0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.17984 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.43
 75zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875zm4.8434753 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836807 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0
 .9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 
 1.453125l0 3.578125l-1.171875 0zm10.664932 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071198 2.65625l-0.125 -1.09375q0.375 0.109375 0.65
 625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625
 q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875
  0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1
 .234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path280"
+       style="fill:#c8ab37" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925z"
+       fill-rule="evenodd"
+       id="path282" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path284" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m140.50394 444.9134c-7.1840515 0 -13.007874 -0.9706116 -13.007874 -2.1679077l0 -97.616974c0 -1.1972961 -5.823822 -2.1678772 -13.007874 -2.1678772l0 0c7.1840515 0 13.007874 -0.9706116 13.007874 -2.1679077l0 -97.61696l0 0c0 -1.1972961 5.823822 -2.1678925 13.007874 -2.1678925"
+       fill-rule="evenodd"
+       id="path286" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m64.91338 302.8189l99.40157 0l0 27.46457l-99.40157 0z"
+       fill-rule="evenodd"
+       id="path288" />
+    <path
+       fill="#000000"
+       d="m74.71026 323.3389l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.
 609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm7.658203 -2.390625l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm6.283203 -3.109375q0 -1.734375 0.953125 -2.5625q0.796875 -0.6875 1.953125 -0.6875q1.28125 0 2.09375 0.84375q0.828125 0.828125 0.828125 2.3125q0 1.203125 -0.359375 1.890625q-0.359375 0.6875 -1.0625 1.078125q-0.6875 0.375 -1.5 0.375q-1.296875 0 -2.109375 -0.828125q-0.796875 -0.84375 -0.796875 -2.421875zm1.078125 0q0 1.1875 0.515625 1.78125q0.53125 0.59375 1.3125 0.59375q0.796875 0 1.3125 -0.59375q0.515625 -0.59375 0.515625 -1.8125q0 -1.15625 -0.53125 -1.75q-0.515625
  -0.59375 -1.296875 -0.59375q-0.78125 0 -1.3125 0.59375q-0.515625 0.578125 -0.515625 1.78125zm10.017578 3.109375l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm10.220703 1.109375l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2
 .390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -
 0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path290" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m687.0105 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path292" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m815.45404 100.022575l0 284.57217"
+       fill-rule="nonzero"
+       id="path294" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 100.52126l129.44092 0"
+       fill-rule="nonzero"
+       id="path296" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 135.71811l129.44092 0"
+       fill-rule="nonzero"
+       id="path298" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 172.91496l129.44092 0"
+       fill-rule="nonzero"
+       id="path300" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 208.11182l129.44092 0"
+       fill-rule="nonzero"
+       id="path302" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 243.30865l129.44092 0"
+       fill-rule="nonzero"
+       id="path304" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 278.50552l129.44092 0"
+       fill-rule="nonzero"
+       id="path306" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 313.70236l129.44092 0"
+       fill-rule="nonzero"
+       id="path308" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 348.8992l129.44092 0"
+       fill-rule="nonzero"
+       id="path310" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m686.51184 384.09607l129.44092 0"
+       fill-rule="nonzero"
+       id="path312" />
+    <path
+       fill="#000000"
+       d="m733.8046 122.32126l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.656982 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 
 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.928101 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.9687
 5 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375z"
+       fill-rule="nonzero"
+       id="path314" />
+    <path
+       fill="#000000"
+       d="m709.2222 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm9.155334 3.0625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm2.5392456 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.9281006 3.453125l-2.125 -6.90625l1.21875 0l1.09375 3.984375l0.421875 1.484375q0.015625 -0.109375 0.359375 -1.421875l1.09375 -4.046875l1.203125 0l1.03125 4.0l0.34375 1.328125l0.40
 625 -1.34375l1.171875 -3.984375l1.140625 0l-2.15625 6.90625l-1.21875 0l-1.09375 -4.140625l-0.265625 -1.171875l-1.40625 5.3125l-1.21875 0zm8.343872 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.60
 9375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.031
 25q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6311035 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.6
 09375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0
 625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q
 -1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.
 53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path316" />
+    <path
+       fill="#000000"
+       d="m710.1765 191.37122l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.234497 -0.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375
  -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6468506 3.453125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm9.974976 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.
 171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874878 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-
 0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.
 296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.2031
 25q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.87
 5 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625z"
+       fill-rule="nonzero"
+       id="path318" />
+    <path
+       fill="#000000"
+       d="m711.206 229.9118l0 -9.546875l6.4375 0l0 1.125l-5.171875 0l0 2.96875l4.46875 0l0 1.125l-4.46875 0l0 4.328125l-1.265625 0zm12.438232 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-
 0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.5218506 1.40625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.
 265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 3.703125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125
  0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm10.865601 2.5625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875
 q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm5.5531006 2.421875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0
 .140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1405029 1.046875l0 -9.546875l1.171875 0l0 3.421875q0.828125 -0.9375 2.078125 -0.9375q0.765625 0 1.328125 0.296875q0.5625 0.296875 0.8125 0.84375q0.25 0.53125 0.25 1.546875l0 4.375l-1.171875 0l0 -4.375q0 -0.890625 -0.390625 -1.28125q-0.375 -0.40625 -1.078125 -0.40625q-0.515625 0 -0.984375 0.28125q-0.453125 0.265625 -0.65625 0.734375q-0.1875 0.453125 -0.1875 1.265625l0 3.78125l-1.171875 0zm15.6310425 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.187
 5q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.188416 -2.21875l1.203125 0.140625q-0.281
 25 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125
  0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875z"
+       fill-rule="nonzero"
+       id="path320" />
+    <path
+       fill="#000000"
+       d="m706.683 265.10867l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.14
 0625l-0.375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765
 625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1
 .90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.8967285 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.56251526 1.71875 -0.56251526q0.78125 0 1.359375 0.3125q0.578125 0.29689026 0.953125 0.89064026q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.
 21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm10.2404785 7.359375l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.
 671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.70314026l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path322" />
+    <path
+       fill="#000000"
+       d="m706.683 300.3055l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.375
  -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0
  2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42
 1875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.3186035 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm7.0217285 2.65625l0 -9.5625l1.07812
 5 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.0
 78125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path324" />
+    <path
+       fill="#000000"
+       d="m706.683 335.50235l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218933 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.37
 5 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 
 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4
 21875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052979 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375
  -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm5.2873535 3.78125l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.7
 5 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248169 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path326" />
+    <path
+       fill="#000000"
+       d="m705.57666 370.69922l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm14.218872 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.7406006 4.125l2.53125 -3.59375l-2.34375 -3.3125l1.46875 0l1.0625 1.609375q0.296875 0.46875 0.484375 0.78125q0.28125 -0.4375 0.515625 -0.765625l1.171875 -1.625l1.40625 0l-2.390625 3.25l2.5625 3.65625l-1.4375 0l-1.421875 -2.140625l-0.
 375 -0.59375l-1.8125 2.734375l-1.421875 0zm10.0078125 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843506 1.046875l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.42187
 5 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1
 .421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm13.1875 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875
  0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm8.912476 2.375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.062
 5 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0z"
+       fill-rule="nonzero"
+       id="path328" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m636.06696 70.291336l230.33069 0l0 27.46457l-230.33069 0z"
+       fill-rule="evenodd"
+       id="path330" />
+    <path
+       fill="#000000"
+       d="m660.59735 92.09134l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm7.0165405 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.874817 -1.171875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.
 96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm5.4437256 6.78125l0 -0.859375l7.765625 0l0 0.859375l-7.765625 0zm8.490601 -2.65625l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm6.9749756 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.9
 0625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.07
 8125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.7873535 0.671875q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0
 .921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm7.7249756 3.453125l-1.078125 0l0 -9.546875l1.171875 0l0 3.40625q0.734375 -0.921875 1.890625 -0.921875q0.640625 0 1.203125 0.265625q0.578125 0.25 0.9375 0.71875q0.375 0.453125 0.578125 1.109375q0.203125 0.65625 0.203125 1.40625q0 1.78125 -0.875 2.75q-0.875 0.96875 -2.109375 0.96875q-1.21875 0 -1.921875 -1.015625l0 0.859375zm0 -3.5q0 1.234375 0.328125 1.78125q0.5625 0.90625 1.5 0.90625q0.765625 0 1.328125 -0.65625q0.5625 -0.671875 0.5625 -2.0q0 -1.34375 -0.546875 -1.984375q-0.53125 -0.65625 -1.296875 -0.65625q-0.765625 0 -1.328125 0.671875q-0.546875 0.671875 -0.546875 1.9375zm6.3343506 -4.6875l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm-1.484375 10.875l0.21875 -1.0q0.359
 375 0.09375 0.546875 0.09375q0.359375 0 0.53125 -0.25q0.1875 -0.234375 0.1875 -1.1875l0 -7.25l1.171875 0l0 7.28125q0 1.28125 -0.328125 1.78125q-0.4375 0.65625 -1.40625 0.65625q-0.484375 0 -0.921875 -0.125zm9.179871 -4.90625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 1.59375l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q
 0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm4.7109375 1.484375l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm4.843445 1.046875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.26
 5625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836853 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.07812
 5zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.664917 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.5
 78125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4
 375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.4923096 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0
 .375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 
 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.7
 03125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path332"
+       style="fill:#008033" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013z"
+       fill-rule="evenodd"
+       id="path334" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path336" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m815.0184 243.91602c7.184082 0 13.007874 2.830719 13.007874 6.3226013l0 53.98471c0 3.4918823 5.8238525 6.3226013 13.007874 6.3226013l0 0c-7.184021 0 -13.007874 2.830719 -13.007874 6.3226013l0 53.98471l0 0c0 3.4918823 -5.8237915 6.3226013 -13.007874 6.3226013"
+       fill-rule="evenodd"
+       id="path338" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m821.979 278.50656l99.40155 0l0 27.46457l-99.40155 0z"
+       fill-rule="evenodd"
+       id="path340" />
+    <path
+       fill="#000000"
+       d="m831.7759 299.02655l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0.765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm7.642578 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.59375 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0
 .609375q-0.5 0.59375 -0.5 1.734375zm4.736328 5.546875l0 -0.765625l7.0 0l0 0.765625l-7.0 0zm11.908203 -4.390625l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm9.908203 3.703125l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q
 0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875zm5.767578 3.625l1.03125 0.15625q0.0625 0.46875 0.359375 0.6875q0.390625 0.296875 1.0625 0.296875q0.734375 0 1.125 -0.296875q0.40625 -0.296875 0.546875 -0.8125q0.09375 -0.328125 0.078125 -1.359375q-0.6875 0.8125 -1.71875 0.8125q-1.28125 0 -1.984375 -0.921875q-0.703125 -0.9375 -0.703125 -2.21875q0 -0.890625 0.3125 -1.640625q0.328125 -0.765625 0.9375 -1.171875q0.609375 -0.40625 1.4375 -0.40625q1.109375 0 1.828125 0.890625l0 -0.75l0.96875 0l0 5.375q0 1.453125 -0.296875 2.0625q-0.296875 0.609375 -0.9375 0.953125q-0.640625 0.359375 -1.578125 0.359375q-1.109375 0 -1.796875 -0.5q-0.6875 -0.5 -0.671875 -1.515625zm0.875 -3.734375q0 1.21875 0.484375 1.7
 8125q0.484375 0.5625 1.21875 0.5625q0.734375 0 1.21875 -0.5625q0.5 -0.5625 0.5 -1.75q0 -1.140625 -0.515625 -1.71875q-0.5 -0.578125 -1.21875 -0.578125q-0.703125 0 -1.203125 0.578125q-0.484375 0.5625 -0.484375 1.6875zm10.251953 1.21875l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.455078 1.84375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.35937
 5 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.0625 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5z"
+       fill-rule="nonzero"
+       id="path342" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m359.4252 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path344" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m520.34906 100.022575l0 247.3753"
+       fill-rule="nonzero"
+       id="path346" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 100.52126l161.92126 0"
+       fill-rule="nonzero"
+       id="path348" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 135.71811l161.92126 0"
+       fill-rule="nonzero"
+       id="path350" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 170.91496l161.92126 0"
+       fill-rule="nonzero"
+       id="path352" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 206.11182l161.92126 0"
+       fill-rule="nonzero"
+       id="path354" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 241.30865l161.92126 0"
+       fill-rule="nonzero"
+       id="path356" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 276.50552l161.92126 0"
+       fill-rule="nonzero"
+       id="path358" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 311.70236l161.92126 0"
+       fill-rule="nonzero"
+       id="path360" />
+    <path
+       stroke="#1155cc"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m358.9265 346.8992l161.92126 0"
+       fill-rule="nonzero"
+       id="path362" />
+    <path
+       fill="#000000"
+       d="m379.84702 119.25876l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.1937256 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359
 375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.802948 1.25q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.4531
 25l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-
 0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.906
 25 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path364" />
+    <path
+       fill="#000000"
+       d="m379.84702 154.45561l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.4
 375q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.92
 1875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0
 .546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2
 .328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.4218
 75 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.615601 4.125l-1.171875 0l0 -7.46875q-0.421875 0.40625 -1.109375 0.8125q-0.6875 0.40625 -1.234375 0.609375l0 -1.140625q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.578125zm6.584198 -3.453125q0 -1.921875 1.07812
 5 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0
 .125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.3437
 5 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.17
 1875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path366" />
+    <path
+       fill="#000000"
+       d="m376.8892 189.65247l1.203125 -0.109375q0.078125 0.71875 0.390625 1.1875q0.3125 0.453125 0.953125 0.734375q0.65625 0.28125 1.46875 0.28125q0.71875 0 1.265625 -0.21875q0.5625 -0.21875 0.828125 -0.578125q0.265625 -0.375 0.265625 -0.828125q0 -0.453125 -0.265625 -0.78125q-0.25 -0.328125 -0.84375 -0.5625q-0.390625 -0.15625 -1.703125 -0.46875q-1.3125 -0.3125 -1.84375 -0.59375q-0.671875 -0.359375 -1.015625 -0.890625q-0.328125 -0.53125 -0.328125 -1.1875q0 -0.71875 0.40625 -1.34375q0.40625 -0.625 1.1875 -0.953125q0.796875 -0.328125 1.765625 -0.328125q1.046875 0 1.859375 0.34375q0.8125 0.34375 1.25 1.015625q0.4375 0.65625 0.46875 1.484375l-1.203125 0.09375q-0.109375 -0.90625 -0.671875 -1.359375q-0.5625 -0.46875 -1.65625 -0.46875q-1.140625 0 -1.671875 0.421875q-0.515625 0.421875 -0.515625 1.015625q0 0.515625 0.359375 0.84375q0.375 0.328125 1.90625 0.6875q1.546875 0.34375 2.109375 0.59375q0.84375 0.390625 1.234375 0.984375q0.390625 0.578125 0.390625 1.359375q0 0.75 -0.4375 1.43
 75q-0.421875 0.671875 -1.25 1.046875q-0.8125 0.359375 -1.828125 0.359375q-1.296875 0 -2.171875 -0.375q-0.875 -0.375 -1.375 -1.125q-0.5 -0.765625 -0.53125 -1.71875zm8.7335205 -0.390625q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921
 875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.9696045 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.
 546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.8828125 0.3125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.
 328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.42187
 5 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.7500305 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921
 875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0
 .484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03
 125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203
 125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path368" />
+    <path
+       fill="#000000"
+       d="m372.88116 227.9118l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.896698 -0.578125q0 -1.6875 0.34375 -2.71875q0.359375 -1.03125 1.046875 -1.59375q0.6875 -0.5625 1.71875 -0.5625q0.78125 0 1.359375 0.3125q0.578125 0.296875 0.953125 0.890625q0.375 0.578125 0.59375 1.421875q0.21875 0.828125 0.21875 2.25q0 1.671875 -0.359375 2.703125q-0.34375 1.03125 -1.03125 1.59375q-0.671875 0.5625 -1.734375 0.5625q-1.375 0 -2.15625 -0.984375q-0.953125 -1.1875 -0.953125 -3.875zm1.203125 0q0 2.34375 0.546875 3.125q0.5625 0.78125 1.359375 0.78125q0.8125 0 1.359375 -0.78125q0.5625 -0.78125 0.5625 -3.125q0 -2.359375 -0.5625 -3.125q-0.546875 -0.78125 -1.359375 -0.78125q-0.8125 0 -1.296875 0.6875q-0.609375 0.875 -0.609375 3.21875zm9.8029785 1.2
 5q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0
 .734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.
 34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm
 9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path370" />
+    <path
+       fill="#000000"
+       d="m372.88116 263.10867l0 -9.54689l6.90625 0l0 1.125l-5.640625 0l0 2.9218903l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.54689l1.296875 0l5.015625 7.5000153l0 -7.5000153l1.203125 0l0 9.54689l-1.296875 0l-5.015625 -7.5000153l0 7.5000153l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.5
 78125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.4218903l1.171875 0l0 9.54689l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.87
 5 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm14.318573 4.125l-1.171875 0l0 -7.4687653q-0.421875 0.40626526 -1.109375 0.81251526q-0.6875 0.40625 -1.234375 0.609375l0 -1.1406403q0.984375 -0.453125 1.71875 -1.109375q0.734375 -0.671875 1.03125 -1.28125l0.765625 0l0 9.57814zm6.5842285 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.6562
 5q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.73439026q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.64064026l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.
 359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.
 15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.7187653l1.171875 -0.703125l0 2.4218903l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625
  0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path372" />
+    <path
+       fill="#000000"
+       d="m372.88116 298.3055l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.717865 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.6
 25q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0
 .578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375
 q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm16.052948 3.0l0 1.125l-6.296875 0q-0.015625 -0.421875 0.140625 -0.8125q0.234375 -0.640625 0.765625 -1.265625q0.53125 -0.625 1.53125 -1.453125q1.5625 -1.265625 2.109375 -2.015625q0.546875 -0.75 0.546875 -1.40625q0 -0.703125 -0.5 -1.171875q-0.5 -0.484375 -1.296875 -0.484375q-0.859375 0 -1.375 0.515625q-0.5 0.5 -0.5 1.390625l-1.203125 -0.109375q0.125 -1.359375 0.921875 -2.0625q0.8125 -0.703125 2.171875 -0.703125q1.375 0 2.171875 0.765625q0.8125 0.75 0.8125 1.875q0 0.578125 -0.234375 1.140625q-0.234375 0.546875 -0.78125 1.15625q-0.546875 0.609375 -1.8125 1.671875q-1.046875 0.890625 -1.359375 1.21875q-0.296875 0.3125 -0.484375 0.625l4.671875 0zm4.8498535 -2.328
 125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 
 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9529724 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-
 0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375
 zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path374" />
+    <path
+       fill="#000000"
+       d="m371.7748 333.50235l0 -9.546875l6.90625 0l0 1.125l-5.640625 0l0 2.921875l5.28125 0l0 1.125l-5.28125 0l0 3.25l5.859375 0l0 1.125l-7.125 0zm8.7178955 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.818726 2.65625l0 -3.390625q-0.265625 0.390625 -0.765625 0.640625q-0.484375 0.25 -1.046875 0.25q-1.21875 0 -2.109375 -0.984375q-0.890625 -0.984375 -0.890625 -2.6875q0 -1.046875 0.359375 -1.875q0.359375 -0.828125 1.046875 -1.25q0.6875 -0.421875 1.515625 -0.421875q1.28125 0 2.015625 1.078125l0 -0.921875l1.046875 0l0 9.5625l-1.171875 0zm-3.609375 -6.125q0 1.328125 0.5625 2.0q0.5625 0.65625 1.34375 0.65625q0.75 0 1.28125 -0.
 625q0.546875 -0.640625 0.546875 -1.9375q0 -1.375 -0.578125 -2.0625q-0.5625 -0.703125 -1.328125 -0.703125q-0.765625 0 -1.296875 0.65625q-0.53125 0.640625 -0.53125 2.015625zm11.146851 3.46875l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q
 0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.053101 4.125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm7.6156006 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.937
 5q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.365448 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm9.047028 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 
 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.131226 3.453125l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm11.365601 1.234375l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q
 0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm10.3654785 4.125l0 -9.546875l1.296875 0l5.015625 7.5l0 -7.5l1.203125 0l0 9.546875l-1.296875 0l-5.015625 -7.5l0 7.5l-1.203125 0zm12.75 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65
 625q-0.578125 0.65625 -0.578125 1.984375zm6.9281006 3.453125l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm3.4620972 0l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.953003 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625
  -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm11.8671875 -0.15625l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0
 .9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm9.084351 3.078125l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875z"
+       fill-rule="nonzero"
+       id="path376" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.77692 65.05774l280.09448 0l0 27.46457l-280.09448 0z"
+       fill-rule="evenodd"
+       id="path378" />
+    <path
+       fill="#000000"
+       d="m345.39603 83.51399l1.265625 0.3125q-0.390625 1.5625 -1.421875 2.375q-1.03125 0.8125 -2.53125 0.8125q-1.53125 0 -2.5 -0.625q-0.96875 -0.625 -1.484375 -1.8125q-0.5 -1.1875 -0.5 -2.5625q0 -1.484375 0.5625 -2.59375q0.578125 -1.109375 1.625 -1.6875q1.0625 -0.578125 2.328125 -0.578125q1.421875 0 2.390625 0.734375q0.984375 0.71875 1.375 2.046875l-1.25 0.296875q-0.328125 -1.046875 -0.96875 -1.515625q-0.625 -0.484375 -1.578125 -0.484375q-1.09375 0 -1.84375 0.53125q-0.734375 0.53125 -1.03125 1.421875q-0.296875 0.875 -0.296875 1.828125q0 1.21875 0.34375 2.125q0.359375 0.90625 1.109375 1.359375q0.75 0.4375 1.625 0.4375q1.0625 0 1.796875 -0.609375q0.734375 -0.609375 0.984375 -1.8125zm2.6876526 -4.84375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.92984 0l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.8593
 75 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.969635 -2.53125l1.15625 0.15625q-0.1875 1.1875 -0.96875 1.859375q-0.78125 0.671875 -1.921875 0.671875q-1.40625 0 -2.28125 -0.921875q-0.859375 -0.9375 -0.859375 -2.65625q0 -1.125 0.375 -1.96875q0.375 -0.84375 1.125 -1.25q0.765625 -0.421875 1.65625 -0.421875q1.125 0 1.84375 0.578125q0.71875 0.5625 0.921875 1.609375l-1.140625 0.171875q-0.171875 -0.703125 -0.59375 -1.046875q-0.40625 -0.359375 -0.984375 -0.359375q-0.890625 0 -1.453125 0.640625q-0.546875 0.640625 -0.546875 2.0q0 1.40625 0.53125 2.03125q0.546875 0.625 1.40625 0.625q0.6875 0 1.140625 -0.421875q0.46875 -0.421875 0.59375 -1.296875zm6.6796875 2.53125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.23
 4375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm2.8656006 0l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0
  1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9749756 3.46875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 0l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578
 125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm15.836792 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0
 .8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.5218506 4.125l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.6649475 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.42187
 5q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm4.4071045 2.65625l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-
 2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm10.398315 -2.65625l0 -9.546875l1.171875 0l0 9.546875l-1.171875 0zm7.49234 -0.859375q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375 -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875
  0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875 -1.171875l0 -0.421875zm2.9437256 6.125l-0.125 -1.09375q0.375 0.109375 0.65625 0.109375q0.390625 0 0.625 -0.140625q0.234375 -0.125 0.390625 -0.359375q0.109375 -0.171875 0.359375 -0.875q0.03125 -0.09375 0.109375 -0.28125l-2.625 -6.921875l1.265625 0l1.4375 4.0q0.28125 0.765625 0.5 1.59375q0.203125 -0.796875 0.46875 -1.578125l1.484375 -4.015625l1.171875 0l-2.625 7.015625q-0.421875 1.140625 -0.65625 1.578125q-0.3125 0.578125 -0.71875 0.84375q-0.40625 0.28125 -0.96875 0.28125q-0.328125 0 -0.75 -0.15625zm6.2734375 -6.109375q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.9218
 75 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.453125 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm11.178101 3.453125l0 -1.015625q-0.8125 1.171875 -2.1875 1.171875q-0.609375 0 -1.140625 -0.234375q-0.53125 -0.234375 -0.796875 -0.578125q-0.25 -0.359375 -0.359375 -0.875q-0.0625 -0.34375 -0.0625 -1.09375l0 -4.28125l1.171875 0l0 3.828125q0 0.921875 0.0625 1.234375q0.109375 0.46875 0.46875 0.734375q0.359375 0.25 0.890625 0.25q0.515625 0 0.984375 -0.265625q0.46875 -0.265625 0.65625 -0.734375q0.1875 -0.46875 0.1875 -1.34375l0 -3.703125l1.171875 0l0 6.90625l-1.046875 0zm5.4437256 -1.046875l0.171875 1.03125q-0.5 0.109375 -0.890625 0.1093
 75q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm5.1247253 1.046875l0 -6.0l-1.03125 0l0 -0.90625l1.03125 0l0 -0.734375q0 -0.703125 0.125 -1.046875q0.171875 -0.453125 0.59375 -0.734375q0.421875 -0.28125 1.203125 -0.28125q0.484375 0 1.09375 0.109375l-0.1875 1.03125q-0.359375 -0.0625 -0.6875 -0.0625q-0.53125 0 -0.75 0.234375q-0.21875 0.21875 -0.21875 0.84375l0 0.640625l1.34375 0l0 0.90625l-1.34375 0l0 6.0l-1.171875 0zm2.9842224 -3.453125q0 -1.921875 1.078125 -2.84375q0.890625 -0.765625 2.171875 -0.765625q1.421875 0 2.328125 0.9375q0.90625 0.921875 0.90625 2.578125q0 1.328125 -0.40625 2.09375q-0.390625 0.765625 -1.15625 1.1875q-0.765625 0.421875 -1.671875 0.421875q-1.4531
 25 0 -2.359375 -0.921875q-0.890625 -0.9375 -0.890625 -2.6875zm1.203125 0q0 1.328125 0.578125 1.984375q0.59375 0.65625 1.46875 0.65625q0.875 0 1.453125 -0.65625q0.578125 -0.671875 0.578125 -2.03125q0 -1.28125 -0.59375 -1.9375q-0.578125 -0.65625 -1.4375 -0.65625q-0.875 0 -1.46875 0.65625q-0.578125 0.65625 -0.578125 1.984375zm6.6312256 3.453125l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm8.156982 2.65625l0 -9.5625l1.078125 0l0 0.890625q0.375 -0.53125 0.84375 -0.78125q0.484375 -0.265625 1.15625 -0.265625q0.875 0 1.546875 0.453125q0.6875 0.453125 1.03125 1.28125q0.34375 0.828125 0.34375 1.828125q0 1.046875 -0.375 1.90625q-0.375 0.84375 -1.109375 1.296875q-0.71875 0.453125 -1.53125 0.453125q-0.578125 0 -1.046875 -0.25q-0.46875 
 -0.25 -0.765625 -0.625l0 3.375l-1.171875 0zm1.0625 -6.078125q0 1.34375 0.53125 1.984375q0.546875 0.625 1.3125 0.625q0.78125 0 1.34375 -0.65625q0.5625 -0.65625 0.5625 -2.046875q0 -1.3125 -0.546875 -1.96875q-0.546875 -0.671875 -1.296875 -0.671875q-0.75 0 -1.328125 0.703125q-0.578125 0.703125 -0.578125 2.03125zm11.084351 1.203125l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm6.521881 4.125l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.187
 5 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm11.896851 0l0 -0.875q-0.65625 1.03125 -1.9375 1.03125q-0.8125 0 -1.515625 -0.453125q-0.6875 -0.453125 -1.078125 -1.265625q-0.375 -0.828125 -0.375 -1.890625q0 -1.03125 0.34375 -1.875q0.34375 -0.84375 1.03125 -1.28125q0.703125 -0.453125 1.546875 -0.453125q0.625 0 1.109375 0.265625q0.5 0.25 0.796875 0.671875l0 -3.421875l1.171875 0l0 9.546875l-1.09375 0zm-3.703125 -3.453125q0 1.328125 0.5625 1.984375q0.5625 0.65625 1.328125 0.65625q0.765625 0 1.296875 -0.625q0.53125 -0.625 0.53125 -1.90625q0 -1.421875 -0.546875 -2.078125q-0.546875 -0.671875 -1.34375 -0.671875q-0.78125 0 -1.3125 0.640625q-0.515625 0.625 -0.515625 2.0zm6.6468506 -4.734
 375l0 -1.359375l1.171875 0l0 1.359375l-1.171875 0zm0 8.1875l0 -6.90625l1.171875 0l0 6.90625l-1.171875 0zm2.9454346 0l0 -6.90625l1.0625 0l0 0.984375q0.75 -1.140625 2.1875 -1.140625q0.625 0 1.15625 0.21875q0.53125 0.21875 0.78125 0.59375q0.265625 0.359375 0.375 0.859375q0.0625 0.328125 0.0625 1.140625l0 4.25l-1.171875 0l0 -4.203125q0 -0.71875 -0.140625 -1.0625q-0.140625 -0.359375 -0.484375 -0.5625q-0.34375 -0.21875 -0.8125 -0.21875q-0.75 0 -1.296875 0.46875q-0.546875 0.46875 -0.546875 1.796875l0 3.78125l-1.171875 0zm7.1937256 0.578125l1.140625 0.15625q0.078125 0.53125 0.40625 0.78125q0.4375 0.3125 1.1875 0.3125q0.8125 0 1.25 -0.328125q0.453125 -0.3125 0.609375 -0.90625q0.09375 -0.359375 0.078125 -1.5q-0.765625 0.90625 -1.90625 0.90625q-1.4375 0 -2.21875 -1.03125q-0.78125 -1.03125 -0.78125 -2.46875q0 -0.984375 0.359375 -1.8125q0.359375 -0.84375 1.03125 -1.296875q0.6875 -0.453125 1.609375 -0.453125q1.21875 0 2.015625 0.984375l0 -0.828125l1.078125 0l0 5.96875q0 1.609375 -0.328125
  2.28125q-0.328125 0.6875 -1.046875 1.078125q-0.703125 0.390625 -1.75 0.390625q-1.234375 0 -2.0 -0.5625q-0.75 -0.5625 -0.734375 -1.671875zm0.984375 -4.15625q0 1.359375 0.53125 1.984375q0.546875 0.625 1.359375 0.625q0.796875 0 1.34375 -0.625q0.546875 -0.625 0.546875 -1.953125q0 -1.265625 -0.5625 -1.90625q-0.5625 -0.640625 -1.359375 -0.640625q-0.765625 0 -1.3125 0.640625q-0.546875 0.625 -0.546875 1.875zm9.8811035 1.515625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.25 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.
 078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625zm9.6953125 1.015625l0.171875 1.03125q-0.5 0.109375 -0.890625 0.109375q-0.640625 0 -1.0 -0.203125q-0.34375 -0.203125 -0.484375 -0.53125q-0.140625 -0.328125 -0.140625 -1.390625l0 -3.96875l-0.859375 0l0 -0.90625l0.859375 0l0 -1.71875l1.171875 -0.703125l0 2.421875l1.171875 0l0 0.90625l-1.171875 0l0 4.046875q0 0.5 0.046875 0.640625q0.0625 0.140625 0.203125 0.234375q0.140625 0.078125 0.40625 0.078125q0.203125 0 0.515625 -0.046875zm1.1248779 1.046875l0 -6.90625l1.0625 0l0 1.046875q0.40625 -0.734375 0.734375 -0.96875q0.
 34375 -0.234375 0.765625 -0.234375q0.59375 0 1.203125 0.375l-0.40625 1.078125q-0.4375 -0.25 -0.859375 -0.25q-0.390625 0 -0.703125 0.234375q-0.296875 0.234375 -0.421875 0.640625q-0.203125 0.625 -0.203125 1.359375l0 3.625l-1.171875 0zm9.1883545 -2.21875l1.203125 0.140625q-0.28125 1.0625 -1.0625 1.65625q-0.765625 0.578125 -1.96875 0.578125q-1.515625 0 -2.40625 -0.9375q-0.890625 -0.9375 -0.890625 -2.609375q0 -1.75 0.890625 -2.703125q0.90625 -0.96875 2.34375 -0.96875q1.390625 0 2.265625 0.9375q0.875 0.9375 0.875 2.65625q0 0.109375 0 0.3125l-5.15625 0q0.0625 1.140625 0.640625 1.75q0.578125 0.59375 1.4375 0.59375q0.65625 0 1.109375 -0.328125q0.453125 -0.34375 0.71875 -1.078125zm-3.84375 -1.90625l3.859375 0q-0.078125 -0.859375 -0.4375 -1.296875q-0.5625 -0.6875 -1.453125 -0.6875q-0.8125 0 -1.359375 0.546875q-0.546875 0.53125 -0.609375 1.4375zm11.037476 3.265625q-0.65625 0.5625 -1.265625 0.796875q-0.59375 0.21875 -1.28125 0.21875q-1.140625 0 -1.75 -0.546875q-0.609375 -0.5625 -0.609375
  -1.4375q0 -0.5 0.21875 -0.921875q0.234375 -0.421875 0.609375 -0.671875q0.375 -0.25 0.84375 -0.390625q0.34375 -0.078125 1.046875 -0.171875q1.421875 -0.171875 2.09375 -0.40625q0 -0.234375 0 -0.296875q0 -0.71875 -0.328125 -1.015625q-0.453125 -0.390625 -1.34375 -0.390625q-0.8125 0 -1.21875 0.296875q-0.390625 0.28125 -0.578125 1.015625l-1.140625 -0.15625q0.15625 -0.734375 0.515625 -1.1875q0.359375 -0.453125 1.03125 -0.6875q0.671875 -0.25 1.5625 -0.25q0.890625 0 1.4375 0.203125q0.5625 0.203125 0.8125 0.53125q0.265625 0.3125 0.375 0.796875q0.046875 0.296875 0.046875 1.078125l0 1.5625q0 1.625 0.078125 2.0625q0.078125 0.4375 0.296875 0.828125l-1.21875 0q-0.1875 -0.359375 -0.234375 -0.859375zm-0.09375 -2.609375q-0.640625 0.265625 -1.921875 0.4375q-0.71875 0.109375 -1.015625 0.25q-0.296875 0.125 -0.46875 0.375q-0.15625 0.25 -0.15625 0.546875q0 0.46875 0.34375 0.78125q0.359375 0.3125 1.046875 0.3125q0.671875 0 1.203125 -0.296875q0.53125 -0.296875 0.78125 -0.8125q0.1875 -0.390625 0.1875
  -1.171875l0 -0.421875zm2.9906006 3.46875l0 -6.90625l1.046875 0l0 0.96875q0.328125 -0.515625 0.859375 -0.8125q0.546875 -0.3125 1.234375 -0.3125q0.78125 0 1.265625 0.3125q0.484375 0.3125 0.6875 0.890625q0.828125 -1.203125 2.140625 -1.203125q1.03125 0 1.578125 0.578125q0.5625 0.5625 0.5625 1.734375l0 4.75l-1.171875 0l0 -4.359375q0 -0.703125 -0.125 -1.0q-0.109375 -0.3125 -0.40625 -0.5q-0.296875 -0.1875 -0.703125 -0.1875q-0.71875 0 -1.203125 0.484375q-0.484375 0.484375 -0.484375 1.546875l0 4.015625l-1.171875 0l0 -4.484375q0 -0.78125 -0.296875 -1.171875q-0.28125 -0.390625 -0.921875 -0.390625q-0.5 0 -0.921875 0.265625q-0.421875 0.25 -0.609375 0.75q-0.1875 0.5 -0.1875 1.453125l0 3.578125l-1.171875 0zm10.633667 -2.0625l1.15625 -0.1875q0.109375 0.703125 0.546875 1.078125q0.453125 0.359375 1.25 0.359375q0.8125 0 1.203125 -0.328125q0.390625 -0.328125 0.390625 -0.765625q0 -0.390625 -0.359375 -0.625q-0.234375 -0.15625 -1.1875 -0.390625q-1.296875 -0.328125 -1.796875 -0.5625q-0.484375 -0.2
 5 -0.75 -0.65625q-0.25 -0.421875 -0.25 -0.9375q0 -0.453125 0.203125 -0.84375q0.21875 -0.40625 0.578125 -0.671875q0.28125 -0.1875 0.75 -0.328125q0.46875 -0.140625 1.015625 -0.140625q0.8125 0 1.421875 0.234375q0.609375 0.234375 0.90625 0.640625q0.296875 0.390625 0.40625 1.0625l-1.140625 0.15625q-0.078125 -0.53125 -0.453125 -0.828125q-0.375 -0.3125 -1.0625 -0.3125q-0.8125 0 -1.15625 0.265625q-0.34375 0.265625 -0.34375 0.625q0 0.234375 0.140625 0.421875q0.15625 0.1875 0.453125 0.3125q0.171875 0.0625 1.03125 0.296875q1.25 0.328125 1.734375 0.546875q0.5 0.203125 0.78125 0.609375q0.28125 0.40625 0.28125 1.0q0 0.59375 -0.34375 1.109375q-0.34375 0.515625 -1.0 0.796875q-0.640625 0.28125 -1.453125 0.28125q-1.34375 0 -2.046875 -0.5625q-0.703125 -0.5625 -0.90625 -1.65625z"
+       fill-rule="nonzero"
+       id="path380"
+       style="fill:#88aa00" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m516.2336 178.6483l115.4646 0l0 27.464554l-115.4646 0z"
+       fill-rule="evenodd"
+       id="path382" />
+    <path
+       fill="#000000"
+       d="m532.2961 196.15266l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm5.9960938 -1.859375l1.03125 -0.15625q0.09375 0.625 0.484375 0.953125q0.40625 0.328125 1.140625 0.328125q0.71875 0 1.0625 -0.28125q0.359375 -0.296875 0.359375 -0.703125q0 -0.359375 -0.3125 -0.5625q-0.21875 -0.140625 -1.078125 -0.359375q-1.15625 -0.296875 -1.609375 -0.5q-0.4375 -0.21875 -0.671875 -0.59375q-0.234375 -0.375 -0.234375 -0.84375q0 -0.40625 0.1875 -0.765625q0.1875 -0.359375 0.515625 -0.59375q0.25 -0.171875 0.671875 -0.296875q0.421875 -0.125 0.921875 -0.125q0.71875 0 1.265625 0.21875q0.5625 0.203125 0.828125 0.5625q0.265625 0.359375 0.359375 0.953125l-1.03125 0.140625q-0.0625 -0.46875 -0.40625 -0.734375q-0.328125 -0.28125 -0.953125 -0.28125q-0.71875 0 -1.03125 0.25q-0.3125 0.234375 -0.3125 0.5625q0 0.203125 0.125 0.359375q0.140625 0.171875 0.40625 0.28125q0.15625 0.062
 5 0.9375 0.265625q1.125 0.3125 1.5625 0.5q0.4375 0.1875 0.6875 0.546875q0.25 0.359375 0.25 0.90625q0 0.53125 -0.3125 1.0q-0.296875 0.453125 -0.875 0.71875q-0.578125 0.25 -1.3125 0.25q-1.21875 0 -1.859375 -0.5q-0.625 -0.515625 -0.796875 -1.5zm8.71875 0.921875l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125zm5.0996094 0.171875q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.
 265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.46875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.35937
 5 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm6.3085938 -0.9375l0.15625 0.921875q-0.453125 0.09375 -0.796875 0.09375q-0.578125 0 -0.890625 -0.171875q-0.3125 -0.1875 -0.453125 -0.484375q-0.125 -0.296875 -0.125 -1.25l0 -3.578125l-0.765625 0l0 -0.8125l0.765625 0l0 -1.546875l1.046875 -0.625l0 2.171875l1.0625 0l0 0.8125l-1.0625 0l0 3.640625q0 0.453125 0.046875 0.578125q0.0625 0.125 0.1875 0.203125q0.125 0.078125 0.359375 0.078125q0.1875 0 0.46875 -0.03125z"
+       fill-rule="nonzero"
+       id="path384" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m589.60895 206.11285l-61.259888 0"
+       fill-rule="evenodd"
+       id="path386" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m589.60895 206.11285l-55.259888 0"
+       fill-rule="evenodd"
+       id="path388"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m534.34906 204.46114l-4.538086 1.6517181l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path390" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m520.7349 322.6483l115.46454 0l0 27.46457l-115.46454 0z"
+       fill-rule="evenodd"
+       id="path392" />
+    <path
+       fill="#000000"
+       d="m536.7974 340.15268l1.125 0.296875q-0.359375 1.390625 -1.28125 2.125q-0.921875 0.734375 -2.265625 0.734375q-1.390625 0 -2.265625 -0.5625q-0.875 -0.5625 -1.328125 -1.625q-0.453125 -1.078125 -0.453125 -2.3125q0 -1.34375 0.515625 -2.34375q0.515625 -1.0 1.453125 -1.515625q0.953125 -0.515625 2.09375 -0.515625q1.28125 0 2.15625 0.65625q0.890625 0.65625 1.234375 1.84375l-1.125 0.265625q-0.296875 -0.9375 -0.875 -1.359375q-0.5625 -0.4375 -1.421875 -0.4375q-0.984375 0 -1.65625 0.484375q-0.65625 0.46875 -0.9375 1.265625q-0.265625 0.796875 -0.265625 1.65625q0 1.09375 0.3125 1.90625q0.328125 0.8125 1.0 1.21875q0.671875 0.40625 1.46875 0.40625q0.953125 0 1.609375 -0.546875q0.671875 -0.546875 0.90625 -1.640625zm2.4003906 -4.359375l0 -1.21875l1.0625 0l0 1.21875l-1.0625 0zm0 7.375l0 -6.21875l1.0625 0l0 6.21875l-1.0625 0zm2.6503906 0l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.39
 0625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.074219 -2.28125l1.03125 0.140625q-0.171875 1.0625 -0.875 1.671875q-0.703125 0.609375 -1.71875 0.609375q-1.28125 0 -2.0625 -0.828125q-0.765625 -0.84375 -0.765625 -2.40625q0 -1.0 0.328125 -1.75q0.34375 -0.765625 1.015625 -1.140625q0.6875 -0.375 1.5 -0.375q1.0 0 1.640625 0.515625q0.65625 0.5 0.84375 1.453125l-1.03125 0.15625q-0.140625 -0.625 -0.515625 -0.9375q-0.375 -0.328125 -0.90625 -0.328125q-0.796875 0 -1.296875 0.578125q-0.5 0.5625 -0.5 1.796875q0 1.265625 0.484375 1.828125q0.484375 0.5625 1.25 0.5625q0.625 0 1.03125 -0.375q0.421875 -0.375 0.546875 -1.171875zm6.015625 2.28125l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0
 .09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.5644531 0l0 -8.59375l1.0625 0l0 8.59375l-1.0625 0zm6.7597656 -0.765625q-0.59375 0.5 -1.140625 0.703125q-0.53125 0.203125 -1.15625 0.203125q-1.03125 0 -1.578125 -0.5q-0.546875 -0.5 -0.546875 -1.28125q0 -0.453125 0.203125 -0.828125q0.203125 -0.390625 0.546875 -0.609375q0.34375 -0.234375 0.765625 -0.34375q0.296875 -0.09375 0.9375 -0.171875q1.265625 -0.140625 1.875 -0.359375q0 -0.21875 0 -0.265625q0 -0.65625 -0.296875 -0.921875q-0.40625 -0.34375 -1.203125 -0.34375q-0.734375 0 -1.09375 0.265625q-0.359375 0.25 -0.53125 0.90625l-1.03125 -0.140625q0.140625 -0.65625 0.46875 -1.0625q0.328125 -0.40625 0.9375 -0.625q0.609375 -0.21875 1.40625 -0.21875q0.796875 0 1.296875 0.1875q0.5 0.1875 0.734375 0.46875q0.234375 0.28125 0.328125 0.71875q0.046875 0.265625 0.046875 0.96875l0 1.40625q0 1.4
 6875 0.0625 1.859375q0.078125 0.390625 0.28125 0.75l-1.109375 0q-0.15625 -0.328125 -0.203125 -0.765625zm-0.09375 -2.359375q-0.578125 0.234375 -1.71875 0.40625q-0.65625 0.09375 -0.921875 0.21875q-0.265625 0.109375 -0.421875 0.328125q-0.140625 0.21875 -0.140625 0.5q0 0.421875 0.3125 0.703125q0.328125 0.28125 0.9375 0.28125q0.609375 0 1.078125 -0.265625q0.484375 -0.265625 0.703125 -0.734375q0.171875 -0.359375 0.171875 -1.046875l0 -0.390625zm2.6894531 3.125l0 -6.21875l0.953125 0l0 0.9375q0.359375 -0.65625 0.65625 -0.859375q0.3125 -0.21875 0.6875 -0.21875q0.53125 0 1.078125 0.328125l-0.359375 0.984375q-0.390625 -0.234375 -0.765625 -0.234375q-0.359375 0 -0.640625 0.21875q-0.265625 0.203125 -0.375 0.578125q-0.1875 0.5625 -0.1875 1.21875l0 3.265625l-1.046875 0zm8.314453 0l-0.984375 0l0 -8.59375l1.0625 0l0 3.0625q0.671875 -0.828125 1.703125 -0.828125q0.578125 0 1.078125 0.234375q0.515625 0.21875 0.84375 0.640625q0.34375 0.421875 0.53125 1.015625q0.1875 0.59375 0.1875 1.265625q0 1.593
 75 -0.796875 2.46875q-0.796875 0.875 -1.890625 0.875q-1.109375 0 -1.734375 -0.921875l0 0.78125zm-0.015625 -3.15625q0 1.109375 0.3125 1.609375q0.5 0.8125 1.34375 0.8125q0.6875 0 1.1875 -0.59375q0.515625 -0.59375 0.515625 -1.796875q0 -1.21875 -0.484375 -1.796875q-0.484375 -0.578125 -1.171875 -0.578125q-0.6875 0 -1.203125 0.609375q-0.5 0.59375 -0.5 1.734375zm9.798828 3.15625l0 -0.921875q-0.734375 1.0625 -1.984375 1.0625q-0.546875 0 -1.03125 -0.203125q-0.46875 -0.21875 -0.703125 -0.53125q-0.234375 -0.328125 -0.328125 -0.796875q-0.0625 -0.296875 -0.0625 -0.984375l0 -3.84375l1.0625 0l0 3.453125q0 0.8125 0.0625 1.109375q0.09375 0.40625 0.40625 0.65625q0.328125 0.234375 0.8125 0.234375q0.46875 0 0.875 -0.234375q0.421875 -0.25 0.59375 -0.671875q0.1875 -0.421875 0.1875 -1.21875l0 -3.328125l1.046875 0l0 6.21875l-0.9375 0zm2.8457031 0l0 -5.40625l-0.9375 0l0 -0.8125l0.9375 0l0 -0.671875q0 -0.625 0.109375 -0.921875q0.15625 -0.421875 0.53125 -0.671875q0.390625 -0.25 1.078125 -0.25q0.453125
  0 0.984375 0.109375l-0.15625 0.90625q-0.328125 -0.046875 -0.625 -0.046875q-0.484375 0 -0.6875 0.203125q-0.1875 0.203125 -0.1875 0.765625l0 0.578125l1.21875 0l0 0.8125l-1.21875 0l0 5.40625l-1.046875 0zm10.667969 -2.0l1.09375 0.125q-0.25 0.953125 -0.953125 1.484375q-0.703125 0.53125 -1.78125 0.53125q-1.359375 0 -2.171875 -0.84375q-0.796875 -0.84375 -0.796875 -2.359375q0 -1.5625 0.8125 -2.421875q0.8125 -0.875 2.09375 -0.875q1.25 0 2.03125 0.84375q0.796875 0.84375 0.796875 2.390625q0 0.09375 0 0.28125l-4.640625 0q0.0625 1.03125 0.578125 1.578125q0.515625 0.53125 1.296875 0.53125q0.578125 0 0.984375 -0.296875q0.421875 -0.3125 0.65625 -0.96875zm-3.453125 -1.703125l3.46875 0q-0.0625 -0.796875 -0.390625 -1.1875q-0.515625 -0.609375 -1.3125 -0.609375q-0.734375 0 -1.234375 0.484375q-0.484375 0.484375 -0.53125 1.3125zm5.876953 3.703125l0 -6.21875l0.9375 0l0 0.875q0.6875 -1.015625 1.984375 -1.015625q0.5625 0 1.03125 0.203125q0.484375 0.203125 0.71875 0.53125q0.234375 0.328125 0.328125 0
 .765625q0.046875 0.296875 0.046875 1.03125l0 3.828125l-1.046875 0l0 -3.78125q0 -0.65625 -0.125 -0.96875q-0.125 -0.3125 -0.4375 -0.5q-0.3125 -0.203125 -0.734375 -0.203125q-0.671875 0 -1.171875 0.4375q-0.484375 0.421875 -0.484375 1.609375l0 3.40625l-1.046875 0zm10.705078 0l0 -0.78125q-0.59375 0.921875 -1.734375 0.921875q-0.75 0 -1.375 -0.40625q-0.625 -0.421875 -0.96875 -1.15625q-0.34375 -0.734375 -0.34375 -1.6875q0 -0.921875 0.3125 -1.6875q0.3125 -0.765625 0.9375 -1.15625q0.625 -0.40625 1.390625 -0.40625q0.5625 0 1.0 0.234375q0.4375 0.234375 0.71875 0.609375l0 -3.078125l1.046875 0l0 8.59375l-0.984375 0zm-3.328125 -3.109375q0 1.203125 0.5 1.796875q0.5 0.578125 1.1875 0.578125q0.6875 0 1.171875 -0.5625q0.484375 -0.5625 0.484375 -1.71875q0 -1.28125 -0.5 -1.875q-0.484375 -0.59375 -1.203125 -0.59375q-0.703125 0 -1.171875 0.578125q-0.46875 0.5625 -0.46875 1.796875z"
+       fill-rule="nonzero"
+       id="path394" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m586.3438 346.90027l-61.259827 0"
+       fill-rule="evenodd"
+       id="path396" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m586.3438 346.90027l-55.259827 0"
+       fill-rule="evenodd"
+       id="path398"
+       style="fill:#00ffcc" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m531.084 345.24854l-4.538086 1.6517334l4.538086 1.6517334z"
+       fill-rule="evenodd"
+       id="path400" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.45407 204.69554l85.51181 -102.645676"
+       fill-rule="evenodd"
+       id="path402" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.45407 204.69554l81.67139 -98.03577"
+       fill-rule="evenodd"
+       id="path404" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m354.39453 107.716995l1.6356201 -4.5439224l-4.1737366 2.4294815z"
+       fill-rule="evenodd"
+       id="path406" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 240.03412l88.0 105.60629"
+       fill-rule="evenodd"
+       id="path408" />
+    <path
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 240.03412l84.15903 100.99686"
+       fill-rule="evenodd"
+       id="path410" />
+    <path
+       fill="#a61c00"
+       stroke="#a61c00"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m353.94522 342.08835l4.1740417 2.4289856l-1.6362 -4.5437317z"
+       fill-rule="evenodd"
+       id="path412" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 392.81104l411.87402 -290.77167"
+       fill-rule="evenodd"
+       id="path414" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 392.81104l406.9724 -287.31128"
+       fill-rule="evenodd"
+       id="path416" />
+    <path
+       fill="#274e13"
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m678.98016 106.84912l2.7546997 -3.9666214l-4.659912 1.2679062z"
+       fill-rule="evenodd"
+       id="path418" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m271.0551 442.09448l415.9685 -61.102356"
+       fill-rule="evenodd"
+       id="path420" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="1.0,3.0"
+       d="m271.0551 442.09448l410.03223 -60.230347"
+       fill-rule="evenodd"
+       id="path422" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m681.3274 383.49832l4.249878 -2.2937317l-4.7299805 -0.9746704z"
+       fill-rule="evenodd"
+       id="path424" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/img/link_the_nodes.svg b/doc/guides/prog_guide/img/link_the_nodes.svg
new file mode 100644
index 000000000..4a127e67c
--- /dev/null
+++ b/doc/guides/prog_guide/img/link_the_nodes.svg
@@ -0,0 +1,3330 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<!-- SPDX-License-Identifier: BSD-3-Clause -->
+<!-- Copyright(C) 2020 Marvell International Ltd. -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   viewBox="0.0 0.0 960.0 540.0"
+   fill="none"
+   stroke="none"
+   stroke-linecap="square"
+   stroke-miterlimit="10"
+   id="svg1003"
+   sodipodi:docname="Link_the_nodes.svg"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
+  <metadata
+     id="metadata1009">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs1007" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="3840"
+     inkscape:window-height="2115"
+     id="namedview1005"
+     showgrid="false"
+     inkscape:zoom="1.2361274"
+     inkscape:cx="1097.0658"
+     inkscape:cy="171.4074"
+     inkscape:window-x="-13"
+     inkscape:window-y="-13"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1003" />
+  <clipPath
+     id="g7c2ed6c54d_0_300.0">
+    <path
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z"
+       clip-rule="nonzero"
+       id="path2" />
+  </clipPath>
+  <g
+     clip-path="url(#g7c2ed6c54d_0_300.0)"
+     id="g1001">
+    <path
+       fill="#ffffff"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill-rule="evenodd"
+       id="path5" />
+    <path
+       fill="#ffffff"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path7" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m144.01245 272.68896l0 0c0 -22.062683 18.40387 -39.948013 41.1062 -39.948013l0 0c10.902039 0 21.357574 4.2088013 29.066483 11.7005005c7.708908 7.491699 12.039719 17.652634 12.039719 28.247513l0 0c0 22.062653 -18.40387 39.947998 -41.1062 39.947998l0 0c-22.702332 0 -41.1062 -17.885345 -41.1062 -39.947998z"
+       fill-rule="evenodd"
+       id="path9" />
+    <path
+       fill="#ffffff"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path11" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m159.53279 253.34254l51.164215 0l0 44.016678l-51.164215 0z"
+       fill-rule="evenodd"
+       id="path13" />
+    <path
+       fill="#fdf8f8"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path15" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m201.07115 257.6069l7.535965 0l0 4.266388l-7.535965 0z"
+       fill-rule="evenodd"
+       id="path17" />
+    <path
+       fill="#d9ead3"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path19" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m166.92793 260.58737l9.485275 0l0 30.94696l-9.485275 0z"
+       fill-rule="evenodd"
+       id="path21" />
+    <path
+       fill="#cfe2f3"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path23" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 260.58737l14.127426 0l0 17.148834l-14.127426 0z"
+       fill-rule="evenodd"
+       id="path25" />
+    <path
+       fill="#f4cccc"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path27" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m179.53246 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path29" />
+    <path
+       fill="#f4cccc"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path31" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m187.15067 281.622l6.511078 0l0 9.802277l-6.511078 0z"
+       fill-rule="evenodd"
+       id="path33" />
+    <path
+       fill="#eeeeee"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path35" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m176.41716 270.24356l0.7700348 -0.77001953l0 0.38500977l1.5948944 0l0 -0.38500977l0.7700348 0.77001953l-0.7700348 0.77005005l0 -0.38500977l-1.5948944 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path37" />
+    <path
+       fill="#eeeeee"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path39" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m182.73688 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path41" />
+    <path
+       fill="#eeeeee"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path43" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m189.7394 278.3624l0.28134155 -0.28134155l0.28134155 0.28134155l-0.14067078 0l0 2.496643l0.14067078 0l-0.28134155 0.28134155l-0.28134155 -0.28134155l0.14067078 0l0 -2.496643z"
+       fill-rule="evenodd"
+       id="path45" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m199.20312 266.6246l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path47" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482z"
+       fill-rule="evenodd"
+       id="path49" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path51" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m198.64803 291.42285c-0.6270752 0 -1.1354218 -0.56329346 -1.1354218 -1.2581482l0 -8.336975c0 -0.69485474 -0.5083313 -1.2581787 -1.1354065 -1.2581787l0 0c0.6270752 0 1.1354065 -0.56329346 1.1354065 -1.2581482l0 -8.336975l0 0c0 -0.69485474 0.50834656 -1.2581482 1.1354218 -1.2581482"
+       fill-rule="evenodd"
+       id="path53" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m191.56721 277.2064l8.701538 0l0 3.3298645l-8.701538 0z"
+       fill-rule="evenodd"
+       id="path55" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path57" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 271.31995l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path59" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path61" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 274.8712l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path63" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path65" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 278.42242l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path67" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path69" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 281.97366l4.7107086 0l0 4.264282l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path71" />
+    <path
+       fill="#ffffff"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path73" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m202.08557 285.5249l4.7107086 0l0 4.2643127l-4.7107086 0z"
+       fill-rule="evenodd"
+       id="path75" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m145.16678 229.68527l73.13281 0l0 16.320053l-73.13281 0z"
+       fill-rule="evenodd"
+       id="path77" />
+    <path
+       fill="#000000"
+       d="m164.66463 247.24533l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827484 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.54685974 -0.375 -0.85935974 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.82810974 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.9531097 -2.75q0 1.046875 0.43748474 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.42185974 0.5 -0.42185974 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.5
 15625 1.40625q0.46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.78125q0.25 -0.40625 0.6875 -0.65625q0.4375 -0.25 0.984375 -0.25q0.609375 0 1.0 0.265625q0.390625 0.25 0.5625 0.703125q0.65625 -0.96875 1.703125 -0.96875q0.828125 0 1.265625 0.46875q0.4375 0.453125 0.4375 1.390625l0 3.796875l-0.921875 0l0 -3.484375q0 -0.5625 -0.09375 -0.796875q-0.09375 -0.25 -0.34375 -0.40625q-0.234375 -0.15625 -0.546875 -0.15625q-0.59375 0 -0.984375 0.390625q-0.375 0.390625 -0.375 1.25l0 3.203125l-0.9375 0l0 -3.59375q0 -0.625 -0.234375 -0.9375q-0.21875 -0.3125 -0.75 -0.3125q-0.390625 0 -0.734375 0.21875q-0.328125 0.203125 -0.484375 0.609375q-0.140625 0.390625 -0.140625 1.15625l0 2.859375l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path79" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path81" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 192.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path83" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path85" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 173.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path87" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path89" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 177.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path91" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path93" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 180.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path95" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path97" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 180.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path99" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path101" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path103" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path105" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 201.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path107" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path109" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 190.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path111" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path113" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path115" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path117" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 198.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path119" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 186.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path121" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path123" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path125" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 211.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path127" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 197.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path129" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path131" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 191.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path133" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path135" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 194.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path137" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path139" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 198.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path141" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path143" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 201.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path145" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path147" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 205.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path149" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 149.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path151" />
+    <path
+       fill="#000000"
+       d="m286.14026 167.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 3.296875l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path153" />
+    <path
+       fill="#ffffff"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path155" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m264.01245 352.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path157" />
+    <path
+       fill="#ffffff"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path159" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m279.49722 333.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path161" />
+    <path
+       fill="#fdf8f8"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path163" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m321.03262 337.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path165" />
+    <path
+       fill="#d9ead3"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path167" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m286.89185 340.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path169" />
+    <path
+       fill="#cfe2f3"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path171" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 340.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path173" />
+    <path
+       fill="#f4cccc"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path175" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m299.49545 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path177" />
+    <path
+       fill="#f4cccc"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path179" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m307.11313 361.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path181" />
+    <path
+       fill="#eeeeee"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path183" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m296.3804 350.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path185" />
+    <path
+       fill="#eeeeee"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path187" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m302.69965 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path189" />
+    <path
+       fill="#eeeeee"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path191" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m309.70166 358.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path193" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m319.16473 346.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path195" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path197" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path199" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m318.60968 371.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path201" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m311.52936 357.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path203" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path205" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 351.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path207" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path209" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 354.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path211" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path213" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 358.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path215" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path217" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 361.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path219" />
+    <path
+       fill="#ffffff"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path221" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m322.04697 365.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path223" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m265.16678 309.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path225" />
+    <path
+       fill="#000000"
+       d="m286.14026 327.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.840271 0.53125q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.46875 1.578125z"
+       fill-rule="nonzero"
+       id="path227" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path229" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 448.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path231" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path233" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 429.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path235" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path237" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 433.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path239" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path241" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 436.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path243" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path245" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 436.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path247" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path249" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path251" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path253" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 457.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path255" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path257" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 446.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path259" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path261" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path263" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path265" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 454.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path267" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 442.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path269" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path271" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path273" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 467.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path275" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 453.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path277" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path279" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 447.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path281" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path283" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 450.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path285" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path287" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 454.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path289" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path291" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 457.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path293" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path295" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 461.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path297" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 405.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path299" />
+    <path
+       fill="#000000"
+       d="m406.43945 423.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.809021 1.640625l0.921875 -0.140625q0.078125 0.5625 0.4375 0.859375q0.359375 0.296875 1.0 0.296875q0.640625 0 0.953125 -0.265625q0.3125 -0.265625 0.3125 -0.625q0 -0.3125 -0.28125 -0.5q-0.1875 -0.125 -0.953125 -0.3125q-1.03125 -0.265625 -1.4375 -0.453125q-0.390625 -0.1875 -0.59375 -0.515625q-0.203125 -0.34375 -0.203125 -0.75q0 -0.359375 0.171875 -0.671875q0.171875 -0.328125 0.453125 -0.53125q0.21875 -0.15625 0.59375 -0.265625q0.390625 -0.125 0.8125 -0.125q0.65625 0 1.140625 0.1875q0.5 0.1875 0.734375 0.515625q0.234375 0.3125 0.3125 0.859375l-0.90625 0.125q-0.0625 -0.4375 -0.375 -0.671875q-0.296875 -0.234375 -0.828125 -0.234375q-0.65625 0 -0.9375 0.21875q-0.265625 0.203125 -0.265625 0.484375q0 0.1875 0.109375 0.328125
 q0.125 0.15625 0.359375 0.25q0.140625 0.0625 0.828125 0.25q1.0 0.265625 1.390625 0.4375q0.390625 0.15625 0.609375 0.484375q0.234375 0.3125 0.234375 0.796875q0 0.46875 -0.28125 0.890625q-0.265625 0.40625 -0.78125 0.640625q-0.515625 0.21875 -1.171875 0.21875q-1.078125 0 -1.640625 -0.4375q-0.5625 -0.453125 -0.71875 -1.34375z"
+       fill-rule="nonzero"
+       id="path301" />
+    <path
+       fill="#ffffff"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path303" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m384.01245 320.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.2092896 29.063751 11.701874c7.708191 7.4926147 12.038605 17.654755 12.038605 28.250885l0 0c0 22.065277 -18.40213 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path305" />
+    <path
+       fill="#ffffff"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path307" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m399.49722 301.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path309" />
+    <path
+       fill="#fdf8f8"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path311" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m441.03262 305.6095l7.535431 0l0 4.266571l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path313" />
+    <path
+       fill="#d9ead3"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path315" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m406.89185 308.59012l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path317" />
+    <path
+       fill="#cfe2f3"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path319" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 308.59012l14.126434 0l0 17.149567l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path321" />
+    <path
+       fill="#f4cccc"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path323" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m419.49545 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path325" />
+    <path
+       fill="#f4cccc"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path327" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.11313 329.62564l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path329" />
+    <path
+       fill="#eeeeee"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path331" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m416.3804 318.24673l0.77005005 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path333" />
+    <path
+       fill="#eeeeee"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path335" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m422.69965 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path337" />
+    <path
+       fill="#eeeeee"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path339" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m429.70166 326.3659l0.28134155 -0.28134155l0.28131104 0.28134155l-0.14065552 0l0 2.4967957l0.14065552 0l-0.28131104 0.28131104l-0.28134155 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path341" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.16473 314.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path343" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566z"
+       fill-rule="evenodd"
+       id="path345" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path347" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m438.60968 339.42694c-0.6270447 0 -1.1353455 -0.56326294 -1.1353455 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.1353455 -1.2580566l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083008 -1.2580566 1.1353455 -1.2580566"
+       fill-rule="evenodd"
+       id="path349" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m431.52936 325.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path351" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path353" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 319.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path355" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path357" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 322.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path359" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path361" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 326.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path363" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path365" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 329.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path367" />
+    <path
+       fill="#ffffff"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path369" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m442.04697 333.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path371" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m385.16678 277.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path373" />
+    <path
+       fill="#000000"
+       d="m407.32922 295.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.582733 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082733 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.168396 3.296875l0 -5.53125l0.84375 0l0 0.84375q0.328125 -0.59375 0.59375 -0.78125q0.28125 -0.1875 0.609375 -0.1875q0.46875 0 0.953125 0.3125l-0.3125 0.859375q-0.34375 -0.203125 -0.6875 -0.203125q-0.3125 0 -0.5625 0.1875q-0.234375 0.1875 -0.34375 0.515625q-0.15625 0.5 -0.15625 1.09375l0 2.890625l-0.9375 0z"
+       fill-rule="nonzero"
+       id="path375" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path377" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 216.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209305 29.063751 11.701889c7.708191 7.4925995 12.038605 17.65474 12.038605 28.25087l0 0c0 22.065292 -18.40213 39.952744 -41.102356 39.952744l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.952744z"
+       fill-rule="evenodd"
+       id="path379" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path381" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 197.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path383" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path385" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 201.6095l7.535431 0l0 4.2665863l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path387" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path389" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 204.5901l9.484589 0l0 30.948334l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path391" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path393" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 204.5901l14.126434 0l0 17.149582l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path395" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path397" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path399" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path401" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 225.62566l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path403" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path405" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 214.24673l0.77005005 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path407" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path409" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path411" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path413" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 222.36589l0.28134155 -0.2813263l0.28131104 0.2813263l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.2813263l-0.28134155 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path415" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 210.6276l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path417" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path419" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path421" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 235.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.56326294 1.1353455 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path423" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 221.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path425" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path427" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 215.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path429" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path431" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 218.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path433" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path435" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 222.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path437" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path439" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 225.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path441" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path443" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 229.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path445" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 173.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path447" />
+    <path
+       fill="#000000"
+       d="m414.14026 191.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.699646 5.421875l0 -2.71875q-0.21875 0.3125 -0.609375 0.515625q-0.390625 0.203125 -0.828125 0.203125q-0.984375 0 -1.703125 -0.78125q-0.703125 -0.796875 -0.703125 -2.15625q0 -0.828125 0.28125 -1.484375q0.296875 -0.671875 0.84375 -1.015625q0.546875 -0.34375 1.203125 -0.34375q1.03125 0 1.609375 0.875l0 -0.75l0.84375 0l0 7.65625l-0.9375 0zm-2.875 -4.90625q0 1.0625 0.4375 1.609375q0.453125 0.53125 1.078125 0.53125q0.59375 0 1.015625 -0.5q0.4375 -0.515625 0.4375 -1.546875q0 -1.109375 -0.453125 -1.65625q-0.453125 -0.5625 -1.0625 -0.5625q-0.609375 0 -1.03125 0.515625q-0.421875 0.515625 -0.421875 1.609375z"
+       fill-rule="nonzero"
+       id="path449" />
+    <path
+       fill="#ffffff"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path451" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m392.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.9010315 0 21.35559 4.209297 29.063751 11.701897c7.708191 7.492592 12.038605 17.654732 12.038605 28.250862l0 0c0 22.065292 -18.40213 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path453" />
+    <path
+       fill="#ffffff"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path455" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m407.49722 93.34494l51.160553 0l0 44.018646l-51.160553 0z"
+       fill-rule="evenodd"
+       id="path457" />
+    <path
+       fill="#fdf8f8"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path459" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m449.03262 97.6095l7.535431 0l0 4.2665787l-7.535431 0z"
+       fill-rule="evenodd"
+       id="path461" />
+    <path
+       fill="#d9ead3"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path463" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m414.89185 100.59011l9.484589 0l0 30.948326l-9.484589 0z"
+       fill-rule="evenodd"
+       id="path465" />
+    <path
+       fill="#cfe2f3"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path467" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 100.59011l14.126434 0l0 17.149574l-14.126434 0z"
+       fill-rule="evenodd"
+       id="path469" />
+    <path
+       fill="#f4cccc"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path471" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m427.49545 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path473" />
+    <path
+       fill="#f4cccc"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path475" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m435.11313 121.625656l6.51062 0l0 9.802734l-6.51062 0z"
+       fill-rule="evenodd"
+       id="path477" />
+    <path
+       fill="#eeeeee"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path479" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m424.3804 110.24673l0.77005005 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path481" />
+    <path
+       fill="#eeeeee"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path483" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m430.69965 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path485" />
+    <path
+       fill="#eeeeee"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path487" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m437.70166 118.36589l0.28134155 -0.28131866l0.28131104 0.28131866l-0.14065552 0l0 2.496811l0.14065552 0l-0.28131104 0.28131866l-0.28134155 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path489" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m447.16473 106.62759l8.700897 0l0 3.330017l-8.700897 0z"
+       fill-rule="evenodd"
+       id="path491" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719z"
+       fill-rule="evenodd"
+       id="path493" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path495" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m446.60968 131.42694c-0.6270447 0 -1.1353455 -0.5632477 -1.1353455 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.1353455 -1.2580719l0 0c0.6270447 0 1.1353455 -0.5632553 1.1353455 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083008 -1.2580719 1.1353455 -1.2580719"
+       fill-rule="evenodd"
+       id="path497" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m439.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path499" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path501" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path503" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path505" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path507" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path509" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path511" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path513" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path515" />
+    <path
+       fill="#ffffff"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path517" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m450.04697 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path519" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m393.16678 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path521" />
+    <path
+       fill="#000000"
+       d="m414.14026 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895233 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.184021 5.421875l0 -7.65625l0.859375 0l0 0.71875q0.296875 -0.421875 0.671875 -0.625q0.390625 -0.21875 0.921875 -0.21875q0.703125 0 1.25 0.375q0.546875 0.359375 0.8125 1.03125q0.28125 0.65625 0.28125 1.453125q0 0.84375 -0.3125 1.53125q-0.296875 0.671875 -0.875 1.03125q-0.578125 0.359375 -1.21875 0.359375q-0.46875 0 -0.84375 -0.1875q-0.375 -0.203125 -0.609375 -0.515625l0 2.703125l-0.9375 0zm0.84375 -4.859375q0 1.0625 0.4375 1.578125q0.4375 0.515625 1.046875 0.515625q0.625 0 1.0625 -0.53125q0.453125 -0.53125 0.453125 -1.640625q0 -1.046875 -0.4375 -1.578125q-0.4375 -0.53125 -1.046875 -0.53125q-0.59375 0 -1.0625 0.5625q-0.453125 0.5625 -0.453125 1.625z"
+       fill-rule="nonzero"
+       id="path523" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path525" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path527" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path529" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path531" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path533" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path535" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path537" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path539" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path541" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path543" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path545" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path547" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path549" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path551" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path553" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path555" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path557" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path559" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path561" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path563" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path565" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path567" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path569" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path571" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path573" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path575" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path577" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path579" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path581" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path583" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path585" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path587" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path589" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path591" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path593" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path595" />
+    <path
+       fill="#000000"
+       d="m566.14026 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm11.809021 3.296875l0 -0.8125q-0.65625 0.9375 -1.75 0.9375q-0.5 0 -0.921875 -0.1875q-0.421875 -0.1875 -0.625 -0.46875q-0.203125 -0.28125 -0.296875 -0.703125q-0.046875 -0.265625 -0.046875 -0.875l0 -3.421875l0.9375 0l0 3.0625q0 0.734375 0.046875 1.0q0.09375 0.359375 0.375 0.578125q0.296875 0.203125 0.703125 0.203125q0.421875 0 0.796875 -0.203125q0.375 -0.21875 0.515625 -0.59375q0.15625 -0.375 0.15625 -1.078125l0 -2.96875l0.9375 0l0 5.53125l-0.828125 0z"
+       fill-rule="nonzero"
+       id="path597" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path599" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 304.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path601" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path603" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 285.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path605" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path607" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 289.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path609" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path611" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 292.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path613" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path615" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 292.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path617" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path619" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path621" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path623" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 313.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path625" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path627" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 302.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path629" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path631" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path633" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path635" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 310.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path637" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 298.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path639" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path641" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path643" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 323.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path645" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 309.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path647" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path649" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 303.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path651" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path653" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 306.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path655" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path657" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 310.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path659" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path661" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 313.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path663" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path665" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 317.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path667" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 261.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path669" />
+    <path
+       fill="#000000"
+       d="m566.43945 279.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm9.715271 3.296875l-2.09375 -5.53125l0.984375 0l1.1875 3.3125q0.1875 0.53125 0.359375 1.109375q0.109375 -0.4375 0.34375 -1.046875l1.21875 -3.375l0.96875 0l-2.09375 5.53125l-0.875 0z"
+       fill-rule="nonzero"
+       id="path671" />
+    <path
+       fill="#ffffff"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path673" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m544.01245 440.69373l0 0c0 -22.065308 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.2092896 29.063782 11.701874c7.708191 7.4926147 12.038574 17.654755 12.038574 28.250885l0 0c0 22.065277 -18.40216 39.95273 -41.102356 39.95273l0 0c-22.700195 0 -41.102356 -17.887451 -41.102356 -39.95273z"
+       fill-rule="evenodd"
+       id="path675" />
+    <path
+       fill="#ffffff"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path677" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m559.4972 421.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path679" />
+    <path
+       fill="#fdf8f8"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path681" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m601.0326 425.6095l7.5354614 0l0 4.266571l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path683" />
+    <path
+       fill="#d9ead3"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path685" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m566.89185 428.59012l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path687" />
+    <path
+       fill="#cfe2f3"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path689" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 428.59012l14.126404 0l0 17.149567l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path691" />
+    <path
+       fill="#f4cccc"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path693" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.4955 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path695" />
+    <path
+       fill="#f4cccc"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path697" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m587.11316 449.62564l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path699" />
+    <path
+       fill="#eeeeee"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path701" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m576.3804 438.24673l0.77008057 -0.77008057l0 0.38504028l1.5946045 0l0 -0.38504028l0.77008057 0.77008057l-0.77008057 0.77005005l0 -0.38500977l-1.5946045 0l0 0.38500977z"
+       fill-rule="evenodd"
+       id="path703" />
+    <path
+       fill="#eeeeee"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path705" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m582.69965 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path707" />
+    <path
+       fill="#eeeeee"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path709" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m589.70166 446.3659l0.28131104 -0.28134155l0.28137207 0.28134155l-0.14068604 0l0 2.4967957l0.14068604 0l-0.28137207 0.28131104l-0.28131104 -0.28131104l0.14068604 0l0 -2.4967957z"
+       fill-rule="evenodd"
+       id="path711" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m599.16473 434.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path713" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566z"
+       fill-rule="evenodd"
+       id="path715" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path717" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m598.6097 459.42694c-0.62701416 0 -1.135376 -0.56326294 -1.135376 -1.2580566l0 -8.3376465c0 -0.6947937 -0.5083008 -1.2580566 -1.135315 -1.2580566l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580872l0 -8.337616l0 0c0 -0.6948242 0.5083618 -1.2580566 1.135376 -1.2580566"
+       fill-rule="evenodd"
+       id="path719" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.52936 445.20987l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path721" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path723" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 439.32315l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path725" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path727" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 442.87454l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path729" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path731" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 446.42593l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path733" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path735" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 449.97736l4.710388 0l0 4.2644653l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path737" />
+    <path
+       fill="#ffffff"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path739" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m602.04694 453.52875l4.710388 0l0 4.264496l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path741" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m545.1668 397.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path743" />
+    <path
+       fill="#000000"
+       d="m566.43945 415.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm7.559021 3.296875l2.015625 -2.875l-1.859375 -2.65625l1.171875 0l0.84375 1.296875q0.234375 0.375 0.390625 0.625q0.21875 -0.34375 0.40625 -0.609375l0.9375 -1.3125l1.125 0l-1.921875 2.609375l2.0625 2.921875l-1.15625 0l-1.125 -1.71875l-0.296875 -0.46875l-1.453125 2.1875l-1.140625 0z"
+       fill-rule="nonzero"
+       id="path745" />
+    <path
+       fill="#ffffff"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path747" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m536.01245 112.69371l0 0c0 -22.0653 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209297 29.063782 11.701897c7.708191 7.492592 12.038574 17.654732 12.038574 28.250862l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path749" />
+    <path
+       fill="#ffffff"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path751" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m551.4972 93.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path753" />
+    <path
+       fill="#fdf8f8"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path755" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m593.0326 97.6095l7.5354614 0l0 4.2665787l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path757" />
+    <path
+       fill="#d9ead3"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path759" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m558.89185 100.59011l9.484558 0l0 30.948326l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path761" />
+    <path
+       fill="#cfe2f3"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path763" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 100.59011l14.126404 0l0 17.149574l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path765" />
+    <path
+       fill="#f4cccc"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path767" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m571.4955 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path769" />
+    <path
+       fill="#f4cccc"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path771" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m579.11316 121.625656l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path773" />
+    <path
+       fill="#eeeeee"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path775" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m568.3804 110.24673l0.77008057 -0.7700653l0 0.38503265l1.5946045 0l0 -0.38503265l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38503265l-1.5946045 0l0 0.38503265z"
+       fill-rule="evenodd"
+       id="path777" />
+    <path
+       fill="#eeeeee"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path779" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m574.69965 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path781" />
+    <path
+       fill="#eeeeee"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path783" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m581.70166 118.36589l0.28131104 -0.28131866l0.28137207 0.28131866l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.28131866l-0.28131104 -0.28131866l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path785" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m591.16473 106.62759l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path787" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path789" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path791" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m590.6097 131.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337639c0 -0.69480896 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.5632553 1.135315 -1.2580643l0 -8.337631l0 0c0 -0.6948166 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path793" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m583.52936 117.20986l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path795" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path797" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 111.323135l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path799" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path801" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 114.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path803" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path805" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 118.42594l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path807" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path809" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 121.97735l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path811" />
+    <path
+       fill="#ffffff"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path813" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m594.04694 125.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path815" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m537.1668 69.68528l73.13385 0l0 16.314957l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path817" />
+    <path
+       fill="#000000"
+       d="m559.62317 87.24024l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.4687
 5 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082764 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0.
 46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm10.230896 2.453125l0.125 0.828125q-0.390625 0.09375 -0.703125 0.09375q-0.5 0 -0.78125 -0.15625q-0.28125 -0.171875 -0.40625 -0.4375q-0.109375 -0.265625 -0.109375 -1.109375l0 -3.171875l-0.6875 0l0 -0.734375l0.6875 0l0 -1.359375l0.9375 -0.5625l0 1.921875l0.9375 0l0 0.734375l-0.9375 0l0 3.234375q0 0.390625 0.046875 0.515625q0.046875 0.109375 0.15625 0.1875q0.109375 0.0625 0.328125 0.0625q0.15625 0 0.40625 -0.046875z"
+       fill-rule="nonzero"
+       id="path819" />
+    <path
+       fill="#ffffff"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path821" />
+    <path
+       stroke="#274e13"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m728.01245 208.69371l0 0c0 -22.065292 18.40216 -39.95276 41.102356 -39.95276l0 0c10.901001 0 21.35559 4.209305 29.063782 11.701889c7.708191 7.4925995 12.038574 17.65474 12.038574 28.25087l0 0c0 22.065292 -18.40216 39.95276 -41.102356 39.95276l0 0c-22.700195 0 -41.102356 -17.887466 -41.102356 -39.95276z"
+       fill-rule="evenodd"
+       id="path823" />
+    <path
+       fill="#ffffff"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path825" />
+    <path
+       stroke="#cc0000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m743.4972 189.34494l51.160583 0l0 44.018646l-51.160583 0z"
+       fill-rule="evenodd"
+       id="path827" />
+    <path
+       fill="#fdf8f8"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path829" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m785.0326 193.6095l7.5354614 0l0 4.2665863l-7.5354614 0z"
+       fill-rule="evenodd"
+       id="path831" />
+    <path
+       fill="#d9ead3"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path833" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m750.89185 196.5901l9.484558 0l0 30.948334l-9.484558 0z"
+       fill-rule="evenodd"
+       id="path835" />
+    <path
+       fill="#cfe2f3"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path837" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 196.5901l14.126404 0l0 17.149582l-14.126404 0z"
+       fill-rule="evenodd"
+       id="path839" />
+    <path
+       fill="#f4cccc"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path841" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m763.4955 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path843" />
+    <path
+       fill="#f4cccc"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path845" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m771.11316 217.62566l6.510559 0l0 9.802734l-6.510559 0z"
+       fill-rule="evenodd"
+       id="path847" />
+    <path
+       fill="#eeeeee"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path849" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m760.3804 206.24673l0.77008057 -0.7700653l0 0.38502502l1.5946045 0l0 -0.38502502l0.77008057 0.7700653l-0.77008057 0.7700653l0 -0.38504028l-1.5946045 0l0 0.38504028z"
+       fill-rule="evenodd"
+       id="path851" />
+    <path
+       fill="#eeeeee"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path853" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m766.69965 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path855" />
+    <path
+       fill="#eeeeee"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path857" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m773.70166 214.36589l0.28131104 -0.2813263l0.28137207 0.2813263l-0.14068604 0l0 2.496811l0.14068604 0l-0.28137207 0.2813263l-0.28131104 -0.2813263l0.14068604 0l0 -2.496811z"
+       fill-rule="evenodd"
+       id="path859" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m783.16473 202.6276l8.700928 0l0 3.330017l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path861" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719z"
+       fill-rule="evenodd"
+       id="path863" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path865" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m782.6097 227.42694c-0.62701416 0 -1.135376 -0.5632477 -1.135376 -1.2580566l0 -8.337631c0 -0.6948242 -0.5083008 -1.2580719 -1.135315 -1.2580719l0 0c0.62701416 0 1.135315 -0.56326294 1.135315 -1.2580719l0 -8.337631l0 0c0 -0.69480896 0.5083618 -1.2580719 1.135376 -1.2580719"
+       fill-rule="evenodd"
+       id="path867" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m775.52936 213.20987l8.700928 0l0 3.3300018l-8.700928 0z"
+       fill-rule="evenodd"
+       id="path869" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path871" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 207.32314l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path873" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path875" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 210.87454l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path877" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path879" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 214.42595l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path881" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path883" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 217.97734l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path885" />
+    <path
+       fill="#ffffff"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path887" />
+    <path
+       stroke="#4a86e8"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       d="m786.04694 221.52875l4.710388 0l0 4.2644806l-4.710388 0z"
+       fill-rule="evenodd"
+       id="path889" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m729.1668 165.68527l73.13385 0l0 16.314972l-73.13385 0z"
+       fill-rule="evenodd"
+       id="path891" />
+    <path
+       fill="#000000"
+       d="m750.43945 183.24023l0 -5.53125l0.84375 0l0 0.796875q0.609375 -0.921875 1.75 -0.921875q0.5 0 0.921875 0.1875q0.421875 0.171875 0.625 0.46875q0.21875 0.296875 0.296875 0.6875q0.046875 0.265625 0.046875 0.921875l0 3.390625l-0.9375 0l0 -3.359375q0 -0.578125 -0.109375 -0.859375q-0.109375 -0.28125 -0.390625 -0.453125q-0.265625 -0.171875 -0.640625 -0.171875q-0.59375 0 -1.03125 0.390625q-0.4375 0.375 -0.4375 1.4375l0 3.015625l-0.9375 0zm5.5827637 -2.765625q0 -1.53125 0.84375 -2.265625q0.71875 -0.625 1.734375 -0.625q1.140625 0 1.859375 0.75q0.734375 0.75 0.734375 2.0625q0 1.0625 -0.328125 1.6875q-0.3125 0.609375 -0.921875 0.953125q-0.609375 0.328125 -1.34375 0.328125q-1.15625 0 -1.875 -0.734375q-0.703125 -0.75 -0.703125 -2.15625zm0.953125 0q0 1.0625 0.46875 1.59375q0.46875 0.53125 1.15625 0.53125q0.703125 0 1.15625 -0.53125q0.46875 -0.53125 0.46875 -1.625q0 -1.015625 -0.46875 -1.546875q-0.453125 -0.53125 -1.15625 -0.53125q-0.6875 0 -1.15625 0.53125q-0.46875 0.515625 -0.468
 75 1.578125zm8.895264 2.765625l0 -0.703125q-0.515625 0.828125 -1.546875 0.828125q-0.65625 0 -1.21875 -0.359375q-0.546875 -0.375 -0.859375 -1.015625q-0.296875 -0.65625 -0.296875 -1.5q0 -0.828125 0.28125 -1.5q0.28125 -0.6875 0.828125 -1.046875q0.546875 -0.359375 1.234375 -0.359375q0.5 0 0.890625 0.21875q0.390625 0.203125 0.625 0.546875l0 -2.734375l0.9375 0l0 7.625l-0.875 0zm-2.953125 -2.75q0 1.046875 0.4375 1.578125q0.453125 0.53125 1.0625 0.53125q0.609375 0 1.03125 -0.5q0.4375 -0.515625 0.4375 -1.53125q0 -1.140625 -0.4375 -1.671875q-0.4375 -0.53125 -1.078125 -0.53125q-0.609375 0 -1.03125 0.515625q-0.421875 0.5 -0.421875 1.609375zm9.082703 0.96875l0.96875 0.125q-0.234375 0.84375 -0.859375 1.3125q-0.609375 0.46875 -1.578125 0.46875q-1.203125 0 -1.921875 -0.75q-0.703125 -0.75 -0.703125 -2.09375q0 -1.390625 0.71875 -2.15625q0.71875 -0.78125 1.859375 -0.78125q1.109375 0 1.8125 0.765625q0.703125 0.75 0.703125 2.125q0 0.078125 0 0.234375l-4.125 0q0.046875 0.921875 0.515625 1.40625q0
 .46875 0.484375 1.15625 0.484375q0.515625 0 0.875 -0.265625q0.359375 -0.28125 0.578125 -0.875zm-3.078125 -1.515625l3.09375 0q-0.0625 -0.6875 -0.359375 -1.046875q-0.453125 -0.53125 -1.15625 -0.53125q-0.640625 0 -1.09375 0.4375q-0.4375 0.421875 -0.484375 1.140625zm8.137146 5.421875l-0.09375 -0.875q0.296875 0.078125 0.53125 0.078125q0.3125 0 0.5 -0.109375q0.1875 -0.09375 0.3125 -0.28125q0.078125 -0.140625 0.28125 -0.703125q0.03125 -0.078125 0.078125 -0.21875l-2.09375 -5.546875l1.015625 0l1.140625 3.203125q0.234375 0.609375 0.40625 1.28125q0.15625 -0.640625 0.375 -1.265625l1.1875 -3.21875l0.9375 0l-2.109375 5.625q-0.328125 0.90625 -0.515625 1.25q-0.25 0.46875 -0.578125 0.6875q-0.3125 0.21875 -0.765625 0.21875q-0.265625 0 -0.609375 -0.125z"
+       fill-rule="nonzero"
+       id="path893" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m211.71075 275.29062c16.11023 0 24.165344 -13.937012 32.220474 -27.874023c8.055115 -13.937012 16.110214 -27.874008 32.220474 -27.874008"
+       fill-rule="evenodd"
+       id="path895" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m211.71075 275.29062c16.11023 0 24.16536 -13.937012 32.220474 -27.874008c4.0275574 -6.968506 8.055115 -13.937012 13.089554 -19.163391c2.5172424 -2.6131897 5.286194 -4.790848 8.432709 -6.315201c1.5732727 -0.7621918 3.2409363 -1.3610382 5.018738 -1.7693481c0.4444275 -0.10209656 0.89575195 -0.19224548 1.3542175 -0.27009583c0.22921753 -0.038909912 0.4602356 -0.07473755 0.6930847 -0.107437134l0.2121582 -0.028259277"
+       fill-rule="evenodd"
+       id="path897" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m272.7317 219.76288l-1.0499573 1.1945496l3.011078 -1.3208618l-3.1556702 -0.923645z"
+       fill-rule="evenodd"
+       id="path899" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m206.79628 284.1058c0 19.13388 14.29921 28.700806 28.598434 38.26773c14.29921 9.566925 28.59842 19.13385 28.59842 38.2677"
+       fill-rule="evenodd"
+       id="path901" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m206.79628 284.1058c0 19.13385 14.29921 28.700775 28.59842 38.26773c7.1496124 4.7834473 14.299225 9.566925 19.661423 15.546234c2.6810913 2.989685 4.915344 6.2783203 6.4793396 10.015381c0.7819824 1.8685608 1.3963928 3.8492126 1.8153076 5.9606323c0.20947266 1.0557556 0.37008667 2.144165 0.47827148 3.2676392l0.00491333 0.05441284"
+       fill-rule="evenodd"
+       id="path903" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m263.83395 357.21786l-1.1755981 -1.0711365l1.2668762 3.0341797l0.9798584 -3.1386719z"
+       fill-rule="evenodd"
+       id="path905" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m324.40216 194.87454c0 -41.086624 33.811005 -82.17323 67.62204 -82.17323"
+       fill-rule="evenodd"
+       id="path907" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m324.40216 194.87454c0 -20.543304 8.452759 -41.086624 21.131866 -56.494095c6.339569 -7.7037506 13.735748 -14.123528 21.660187 -18.617378c3.9622498 -2.2469177 8.056519 -4.0123596 12.216888 -5.216072c2.0801697 -0.6018524 4.17688 -1.0632782 6.281769 -1.3742294c0.5262451 -0.07774353 1.0529785 -0.14608002 1.5801392 -0.20485687c0.26358032 -0.029388428 0.5272217 -0.056381226 0.7909546 -0.080963135l0.53601074 -0.045051575"
+       fill-rule="evenodd"
+       id="path909" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.6 112.841896l-1.0775146 1.1697693l3.0410461 -1.2503891l-3.1333008 -0.9968872z"
+       fill-rule="evenodd"
+       id="path911" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.75735 207.661c36.834625 0 73.66928 40.34645 73.66928 80.69292"
+       fill-rule="evenodd"
+       id="path913" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.75735 207.66098c18.417297 0 36.834625 10.086624 50.647614 25.216537c6.906494 7.564972 12.661926 16.390747 16.690704 25.84694c2.0144043 4.728119 3.597168 9.613831 4.6762695 14.578339c0.5395813 2.4822388 0.9532471 4.984192 1.2320251 7.4959717c0.1394043 1.2559204 0.24505615 2.5142822 0.31588745 3.7738953l0.017486572 0.35531616"
+       fill-rule="evenodd"
+       id="path915" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m400.33734 284.92798l-1.1535034 -1.0949097l1.2047119 3.0594177l1.0437012 -3.1180115z"
+       fill-rule="evenodd"
+       id="path917" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m331.69748 203.27638c15.078735 0 22.618134 3.3543396 30.157501 6.708664c7.5393677 3.3543396 15.078735 6.708664 30.15747 6.708664"
+       fill-rule="evenodd"
+       id="path919" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m331.69748 203.2764c15.078766 0 22.618134 3.3543243 30.157501 6.7086487c3.7696838 1.6771698 7.5393677 3.3543396 12.251495 4.612213c2.3560486 0.62893677 4.947693 1.1530457 7.8927917 1.519928c1.4725037 0.18344116 3.0333862 0.32757568 4.6973267 0.42582703c0.41601562 0.024597168 0.83847046 0.046279907 1.2675476 0.06503296l0.62176514 0.024765015"
+       fill-rule="evenodd"
+       id="path921" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m388.5859 216.63281l-1.1443787 1.1044312l3.109253 -1.0695038l-3.069275 -1.179306z"
+       fill-rule="evenodd"
+       id="path923" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m323.7046 355.2753c36.17325 0 72.346466 -3.1653748 72.346466 -6.330719"
+       fill-rule="evenodd"
+       id="path925" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m323.7046 355.2753c18.08664 0 36.17325 -0.7913208 49.73819 -1.9783325c6.782501 -0.59350586 12.43457 -1.2859497 16.390991 -2.0278015c0.98913574 -0.18551636 1.8722534 -0.37402344 2.6405945 -0.5649414l0.5545349 -0.14355469"
+       fill-rule="evenodd"
+       id="path927" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m393.02893 350.56067l-0.46139526 1.5220032l2.1943665 -2.4487l-3.2549744 0.4653015z"
+       fill-rule="evenodd"
+       id="path929" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m326.78412 359.65433c0 44.519684 28.614166 89.0394 57.228333 89.0394"
+       fill-rule="evenodd"
+       id="path931" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m326.7841 359.65433c0 22.259827 7.1535645 44.519684 17.88388 61.21457c5.3651733 8.347443 11.624512 15.30365 18.330963 20.172974c3.3532104 2.4346619 6.818207 4.3476562 10.339111 5.6519165c1.760437 0.6521301 3.534851 1.1521301 5.316223 1.4890747c0.44540405 0.084228516 0.8911743 0.15826416 1.3372803 0.22195435l0.60028076 0.0786438"
+       fill-rule="evenodd"
+       id="path933" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m380.59183 448.4835l-1.1914368 1.0534973l3.1529236 -0.9329529l-3.0149841 -1.3119812z"
+       fill-rule="evenodd"
+       id="path935" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.3904 229.5441c22.905518 0 34.358246 -5.2125854 45.811005 -10.425186c11.452759 -5.2126007 22.905518 -10.425201 45.811035 -10.425201"
+       fill-rule="evenodd"
+       id="path937" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.3904 229.54411c22.905518 0 34.358276 -5.2126007 45.811035 -10.425201c5.7263794 -2.606308 11.452759 -5.2126007 18.610748 -7.167328c3.5789795 -0.97735596 7.515869 -1.7918243 11.989563 -2.3619537c2.2368774 -0.2850647 4.607971 -0.50904846 7.13562 -0.6617737c1.263794 -0.07633972 2.5668335 -0.13487244 3.9116821 -0.17433167l0.73657227 -0.018814087"
+       fill-rule="evenodd"
+       id="path939" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58563 208.7347l-1.111084 1.1379547l3.0761108 -1.1614532l-3.1029663 -1.0875549z"
+       fill-rule="evenodd"
+       id="path941" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905518 -39.425186 41.811005 -52.566925c20.905548 -13.141724 41.811066 -26.283463 41.811066 -52.566925"
+       fill-rule="evenodd"
+       id="path943" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m452.40216 225.97734c0 -26.283463 20.905487 -39.4252 41.811005 -52.566925c10.452759 -6.570862 20.905548 -13.141739 28.745087 -21.355316c3.9197998 -4.1067963 7.1862793 -8.624268 9.472839 -13.757751c1.1432495 -2.566742 2.041504 -5.287491 2.6539917 -8.187912c0.30621338 -1.4502106 0.5410156 -2.945343 0.69921875 -4.4886017c0.03955078 -0.38581085 0.07434082 -0.7746353 0.10424805 -1.1665115l0.013000488 -0.18595123"
+       fill-rule="evenodd"
+       id="path945" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m535.90155 124.26837l1.0835571 1.1641159l-1.0132446 -3.1280365l-1.234436 3.0475311z"
+       fill-rule="evenodd"
+       id="path947" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m454.75735 231.661c22.314941 0 33.472443 20.960617 44.629913 41.92125c11.157471 20.960632 22.314941 41.921265 44.629944 41.921265"
+       fill-rule="evenodd"
+       id="path949" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m454.75735 231.66098c22.314941 0 33.472412 20.960632 44.629913 41.921265c5.5787354 10.480316 11.157471 20.960632 18.13092 28.820862c3.4866943 3.9301147 7.3220825 7.2052307 11.680481 9.497803c2.1791992 1.1462708 4.4891357 2.046936 6.95166 2.6610107c1.2312012 0.30703735 2.5006104 0.54241943 3.810852 0.7010803c0.16375732 0.019836426 0.32818604 0.03845215 0.49328613 0.055877686l0.13989258 0.013671875"
+       fill-rule="evenodd"
+       id="path951" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.59436 315.33255l-1.1792603 1.0671082l3.1420288 -0.9690857l-3.0298462 -1.2772827z"
+       fill-rule="evenodd"
+       id="path953" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m446.78412 455.65433c24.307068 0 36.4606 -3.7401428 48.614166 -7.480316c12.153534 -3.7401428 24.307098 -7.4802856 48.614166 -7.4802856"
+       fill-rule="evenodd"
+       id="path955" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m446.78412 455.65433c24.307098 0 36.460632 -3.7401733 48.614166 -7.480316c6.076782 -1.8700867 12.153564 -3.7401733 19.749542 -5.1427307c3.7979736 -0.7012634 7.975708 -1.285675 12.723206 -1.6947632c2.3737793 -0.20455933 4.8898926 -0.36523438 7.5722656 -0.47479248c1.3411255 -0.05480957 2.723816 -0.09680176 4.151062 -0.12512207l0.99108887 -0.017120361"
+       fill-rule="evenodd"
+       id="path957" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m540.58545 440.71948l-1.1160889 1.1329956l3.0812378 -1.1477966l-3.0981445 -1.1012878z"
+       fill-rule="evenodd"
+       id="path959" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-68.0 10.0 -136.0 52.5 -136.0 104.99999"
+       fill-rule="evenodd"
+       id="path961" />
+    <path
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m573.7337 69.68528c0 -12.5 -68.0 -35.0 -136.0 -25.0c-34.0 5.0 -68.0 18.125 -93.5 36.562492c-12.75 9.21875 -23.375 19.765625 -30.8125 31.289062c-3.71875 5.7617188 -6.640625 11.767586 -8.6328125 17.973633c-0.99606323 3.1030273 -1.7597351 6.2561035 -2.274414 9.453735c-0.25732422 1.5988159 -0.45239258 3.2087708 -0.5831299 4.829178c-0.032684326 0.4051056 -0.061340332 0.81085205 -0.0859375 1.2172546l-0.013824463 0.24894714"
+       fill-rule="evenodd"
+       id="path963" />
+    <path
+       fill="#980000"
+       stroke="#980000"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m301.8311 146.25957l-1.0921936 -1.1560669l1.0363464 3.1204681l1.2119141 -3.0565796z"
+       fill-rule="evenodd"
+       id="path965" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m628.3904 213.5441c24.905518 0 37.358276 -1.2125854 49.811035 -2.4251862c12.452759 -1.2126007 24.905518 -2.4252014 49.811035 -2.4252014"
+       fill-rule="evenodd"
+       id="path967" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m628.39044 213.54411c24.905518 0 37.358215 -1.2126007 49.810974 -2.4252014c6.2263794 -0.606308 12.452759 -1.2126007 20.235779 -1.6673279c3.8914795 -0.22735596 8.172058 -0.41682434 13.036499 -0.54945374c2.432129 -0.0663147 5.010254 -0.11842346 7.758606 -0.15393066c1.3741455 -0.01777649 2.7908936 -0.031402588 4.253235 -0.04055786l1.0998535 -0.0051574707"
+       fill-rule="evenodd"
+       id="path969" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m724.5854 208.7025l-1.1217041 1.1274567l3.086914 -1.1324921l-3.0926514 -1.1166687z"
+       fill-rule="evenodd"
+       id="path971" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m622.7841 431.67007c0 -45.75589 36.58264 -68.63385 73.165344 -91.51181c36.582703 -22.87793 73.165344 -45.75589 73.165344 -91.511795"
+       fill-rule="evenodd"
+       id="path973" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m622.7841 431.67007c0 -45.75592 36.582703 -68.63385 73.165344 -91.51178c18.29132 -11.438995 36.582703 -22.87796 50.30121 -37.176697c6.859253 -7.149353 12.575256 -15.013641 16.576538 -23.950348c2.0005493 -4.4683533 3.5724487 -9.204803 4.644226 -14.254028c0.5359497 -2.5246277 0.94677734 -5.1274414 1.2236328 -7.814026c0.13842773 -1.3433075 0.24334717 -2.70755 0.3137207 -4.093445l0.035339355 -0.7969208"
+       fill-rule="evenodd"
+       id="path975" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m769.04407 252.07283l1.1011353 1.1475525l-1.0605469 -3.11232l-1.1881104 3.0658875z"
+       fill-rule="evenodd"
+       id="path977" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m598.7573 117.00678c36.88977 0 55.334656 13.614174 73.77954 27.22834c18.444885 13.6141815 36.88977 27.228348 73.77954 27.228348"
+       fill-rule="evenodd"
+       id="path979" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m598.7573 117.006775c36.88977 0 55.334656 13.6141815 73.77954 27.228348c9.222473 6.807083 18.444946 13.6141815 29.972961 18.719482c5.764038 2.552658 12.104431 4.679886 19.30951 6.16893c3.602478 0.7445221 7.4211426 1.3295135 11.492004 1.728363c2.0354614 0.19943237 4.13385 0.35231018 6.2998657 0.45535278c0.5415039 0.025772095 1.0872803 0.04840088 1.6373291 0.06788635c0.2750244 0.009719849 0.5510864 0.018676758 0.8282471 0.026824951l0.8128052 0.021453857"
+       fill-rule="evenodd"
+       id="path981" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m742.8896 171.42342l-1.1376343 1.1113739l3.1026611 -1.0884094l-3.076416 -1.160614z"
+       fill-rule="evenodd"
+       id="path983" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m451.69748 115.27639c21.078735 0 31.618134 -0.645668 42.1575 -1.2913437c10.539368 -0.645668 21.078735 -1.291336 42.15747 -1.291336"
+       fill-rule="evenodd"
+       id="path985" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m451.6975 115.27639c21.078735 0 31.618103 -0.645668 42.15747 -1.291336c5.269684 -0.32283783 10.539368 -0.645668 17.126495 -0.8877945c3.2935486 -0.12106323 6.9164734 -0.22195435 11.033356 -0.29257202c2.0584717 -0.035308838 4.2404785 -0.063056946 6.5665894 -0.081970215c1.1630249 -0.009460449 2.3620605 -0.016708374 3.5997314 -0.021591187l0.40423584 -7.9345703E-4"
+       fill-rule="evenodd"
+       id="path987" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m532.5854 112.70034l-1.1224365 1.1267548l3.0876465 -1.1305542l-3.09198 -1.1186066z"
+       fill-rule="evenodd"
+       id="path989" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m604.3975 309.543c67.82678 0 135.6535 -36.299225 135.6535 -72.59842"
+       fill-rule="evenodd"
+       id="path991" />
+    <path
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linejoin="round"
+       stroke-linecap="butt"
+       stroke-dasharray="8.0,3.0"
+       d="m604.3975 309.543c33.91339 0 67.82678 -9.074799 93.26178 -22.687012c12.717529 -6.8060913 23.31543 -14.7465515 30.734009 -23.25418c3.7092896 -4.2538147 6.623657 -8.649414 8.610779 -13.115921c0.9935913 -2.233261 1.7553711 -4.4842224 2.2686157 -6.7440643c0.12835693 -0.56495667 0.2411499 -1.1304779 0.3381958 -1.6963959c0.04852295 -0.28297424 0.09307861 -0.5660553 0.13366699 -0.84921265c0.020263672 -0.14157104 0.039611816 -0.28315735 0.057922363 -0.42478943l0.049560547 -0.4055481"
+       fill-rule="evenodd"
+       id="path993" />
+    <path
+       fill="#595959"
+       stroke="#595959"
+       stroke-width="1.0"
+       stroke-linecap="butt"
+       d="m739.852 240.36588l1.0574341 1.1879883l-0.94329834 -3.1498566l-1.302124 3.0192566z"
+       fill-rule="evenodd"
+       id="path995" />
+    <path
+       fill="#000000"
+       fill-opacity="0.0"
+       d="m49.937008 5.086614l504.22046 0l0 27.464567l-504.22046 0z"
+       fill-rule="evenodd"
+       id="path997" />
+    <path
+       fill="#000000"
+       d="m60.296383 32.00661l0 -13.359373l1.78125 0l0 11.78125l6.562496 0l0 1.5781231l-8.343746 0zm10.250713 -11.468748l0 -1.890625l1.640625 0l0 1.890625l-1.640625 0zm0 11.468748l0 -9.671873l1.640625 0l0 9.671873l-1.640625 0zm4.144821 0l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm10.375717 0l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm18.089554 -1.4687481l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -
 1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051788 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.078125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000717 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.437
 5l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324654 5.765623l0 -9.671873l1.46875 0l0 1.375q1.0625 -1.59375 3.078125 -1.59375q0.875 0 1.609375 0.3125q0.734375 0.3125 1.09375 0.828125q0.375 0.5 0.515625 1.203125q0.09375 0.453125 0.09375 1.59375l0 5.953123l-1.640625 0l0 -5.890623q0 -1.0 -0.203125 -1.484375q-0.1875 -0.5 -0.671875 -0.796875q-0.484375 -0.296875 -1.140625 -0.296875q-1.046875 0 -1.8125 0.671875q-0.75 0.65625 -0.75 2.515625l0 5.281248l-1.640625 0zm9.766342 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.296
 8731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm15.563217 4.843748l0 -1.2187481q-0.90625 1.4374981 -2.703125 1.4374981q-1.15625 0 -2.125 -0.6406231q-0.96875 -0.640625 -1.5 -1.78125q-0.53125 -1.140625 -0.53125 -2.625q0 -1.453125 0.484375 -2.625q0.484375 -1.1875 1.4375 -1.8125q0.96875 -0.625 2.171875 -0.625q0.875 0 1.546875 0.375q0.6875 0.359375 1.109375 0.953125l0 -4.796875l1.640625 0l0 13.359373l-1.53125 0zm-5.171875 -4.828123q0 1.859375 0.78125 2.78125q0.78125 0.921875 1.84375 0.921875q1.078125 0 1.828125 -0.875q0.75 -0.890625 0.75 -2.6875q0 -1.984375 -0.765625 -2.90625q-0.765625 -0.9375 -1.890625 -0.9375q-1.078125 0 -1.8125 0.890625q-0.734375 0.890625 -0.734375 2.8125zm15.906967 1.71875l1.6875 0.203125q-0.40625 1.484375 -1.
 484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm8.485092 2.875l1.625 -0.25q0.125 0.96875 0.75 1.5q0.625 0.515625 1.75 0.515625q1.125 0 1.671875 -0.453125q0.546875 -0.46875 0.546875 -1.09375q0 -0.546875 -0.484375 -0.875q-0.328125 -0.21875 -1.671875 -0.546875q-1.8125 -0.46875 -2.515625 -0.796875q-0.6875 -0.328125 -1.046875 -0.90625q-0.359375 -0.59375 -0.359375 -1.3125q0 -0.640625 0.296875 -1.1875q0.296875 -0.5625 0.8125 -0.921875q0.375 -0.28125 1.03125 -0.46875q0.671
 875 -0.203125 1.421875 -0.203125q1.140625 0 2.0 0.328125q0.859375 0.328125 1.265625 0.890625q0.421875 0.5625 0.578125 1.5l-1.609375 0.21875q-0.109375 -0.75 -0.640625 -1.171875q-0.515625 -0.421875 -1.46875 -0.421875q-1.140625 0 -1.625 0.375q-0.46875 0.375 -0.46875 0.875q0 0.3125 0.1875 0.578125q0.203125 0.265625 0.640625 0.4375q0.234375 0.09375 1.4375 0.421875q1.75 0.453125 2.4375 0.75q0.6875 0.296875 1.078125 0.859375q0.390625 0.5625 0.390625 1.40625q0 0.828125 -0.484375 1.546875q-0.46875 0.71875 -1.375 1.125q-0.90625 0.3906231 -2.046875 0.3906231q-1.875 0 -2.875 -0.7812481q-0.984375 -0.78125 -1.25 -2.328125zm18.745804 1.421875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.2
 03125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm0.99580383 -3.375q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm20.793396 1.296875l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3.71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0
 .96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.0 3.546873l0 -9.671873l1.46875 0l0 1.46875q0.5625 -1.03125 1.03125 -1.359375q0.484375 -0.328125 1.0625 -0.328125q0.828125 0 1.6875 0.53125l-0.5625 1.515625q-0.609375 -0.359375 -1.203125 -0.359375q-0.546875 0 -0.96875 0.328125q-0.421875 0.328125 -0.609375 0.890625q-0.28125 0.875 -0.28125 1.921875l0 5.062498l-1.625 0zm12.853302 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.
 484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm15.453842 4.578125q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.421875q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.4
 0625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm7.781967 3.390625l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm8.230179 -1.640625l1.6874847 0.203125q-0.40625 1.484375 -1.4843597 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.6
 71875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.1562347 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.2187347 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm17.902756 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm1.6051941 1.4687481l0 -13.359373l1.640625 0l0 4.796875q1.140625 -1.328125 2.890625 -1.328125q1.07
 8125 0 1.859375 0.421875q0.796875 0.421875 1.140625 1.171875q0.34375 0.75 0.34375 2.171875l0 6.124998l-1.640625 0l0 -6.124998q0 -1.234375 -0.53125 -1.796875q-0.53125 -0.5625 -1.515625 -0.5625q-0.71875 0 -1.359375 0.390625q-0.640625 0.375 -0.921875 1.015625q-0.265625 0.640625 -0.265625 1.78125l0 5.296873l-1.640625 0zm17.000702 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.015625 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm14.324646 9.468748l0 -13.374998l1.484375 0l0 1
 .25q0.53125 -0.734375 1.1875 -1.09375q0.671875 -0.375 1.625 -0.375q1.234375 0 2.171875 0.640625q0.953125 0.625 1.4375 1.796875q0.484375 1.15625 0.484375 2.546875q0 1.484375 -0.53125 2.671875q-0.53125 1.1875 -1.546875 1.828125q-1.015625 0.6249981 -2.140625 0.6249981q-0.8125 0 -1.46875 -0.3437481q-0.65625 -0.34375 -1.0625 -0.875l0 4.703123l-1.640625 0zm1.484375 -8.484373q0 1.859375 0.75 2.765625q0.765625 0.890625 1.828125 0.890625q1.09375 0 1.875 -0.921875q0.78125 -0.9375 0.78125 -2.875q0 -1.84375 -0.765625 -2.765625q-0.75 -0.921875 -1.8125 -0.921875q-1.046875 0 -1.859375 0.984375q-0.796875 0.96875 -0.796875 2.84375zm15.203857 3.59375q-0.921875 0.765625 -1.765625 1.09375q-0.828125 0.3124981 -1.796875 0.3124981q-1.59375 0 -2.453125 -0.7812481q-0.859375 -0.78125 -0.859375 -1.984375q0 -0.71875 0.328125 -1.296875q0.328125 -0.59375 0.84375 -0.9375q0.53125 -0.359375 1.1875 -0.546875q0.46875 -0.125 1.453125 -0.25q1.984375 -0.234375 2.921875 -0.5625q0.015625 -0.34375 0.015625 -0.42187
 5q0 -1.0 -0.46875 -1.421875q-0.625 -0.546875 -1.875 -0.546875q-1.15625 0 -1.703125 0.40625q-0.546875 0.40625 -0.8125 1.421875l-1.609375 -0.21875q0.21875 -1.015625 0.71875 -1.640625q0.5 -0.640625 1.453125 -0.984375q0.953125 -0.34375 2.1875 -0.34375q1.25 0 2.015625 0.296875q0.78125 0.28125 1.140625 0.734375q0.375 0.4375 0.515625 1.109375q0.078125 0.421875 0.078125 1.515625l0 2.1875q0 2.28125 0.109375 2.890625q0.109375 0.59375 0.40625 1.1562481l-1.703125 0q-0.265625 -0.5156231 -0.328125 -1.1874981zm-0.140625 -3.671875q-0.890625 0.375 -2.671875 0.625q-1.015625 0.140625 -1.4375 0.328125q-0.421875 0.1875 -0.65625 0.53125q-0.21875 0.34375 -0.21875 0.78125q0 0.65625 0.5 1.09375q0.5 0.4375 1.453125 0.4375q0.9375 0 1.671875 -0.40625q0.75 -0.421875 1.09375 -1.140625q0.265625 -0.5625 0.265625 -1.640625l0 -0.609375zm10.516357 1.3125l1.609375 0.21875q-0.265625 1.65625 -1.359375 2.609375q-1.078125 0.9374981 -2.671875 0.9374981q-1.984375 0 -3.1875 -1.2968731q-1.203125 -1.296875 -1.203125 -3
 .71875q0 -1.578125 0.515625 -2.75q0.515625 -1.171875 1.578125 -1.75q1.0625 -0.59375 2.3125 -0.59375q1.578125 0 2.578125 0.796875q1.0 0.796875 1.28125 2.265625l-1.59375 0.234375q-0.234375 -0.96875 -0.8125 -1.453125q-0.578125 -0.5 -1.390625 -0.5q-1.234375 0 -2.015625 0.890625q-0.78125 0.890625 -0.78125 2.8125q0 1.953125 0.75 2.84375q0.75 0.875 1.953125 0.875q0.96875 0 1.609375 -0.59375q0.65625 -0.59375 0.828125 -1.828125zm3.015625 3.546873l0 -13.359373l1.640625 0l0 7.625l3.890625 -3.9375l2.109375 0l-3.6875 3.59375l4.0625 6.078123l-2.015625 0l-3.203125 -4.953123l-1.15625 1.125l0 3.828123l-1.640625 0zm15.953125 -3.109373l1.6875 0.203125q-0.40625 1.484375 -1.484375 2.3125q-1.078125 0.8124981 -2.765625 0.8124981q-2.125 0 -3.375 -1.2968731q-1.234375 -1.3125 -1.234375 -3.671875q0 -2.453125 1.25 -3.796875q1.265625 -1.34375 3.265625 -1.34375q1.9375 0 3.15625 1.328125q1.234375 1.3125 1.234375 3.703125q0 0.15625 0 0.4375l-7.21875 0q0.09375 1.59375 0.90625 2.453125q0.8125 0.84375 2.01562
 5 0.84375q0.90625 0 1.546875 -0.46875q0.640625 -0.484375 1.015625 -1.515625zm-5.390625 -2.65625l5.40625 0q-0.109375 -1.21875 -0.625 -1.828125q-0.78125 -0.953125 -2.03125 -0.953125q-1.125 0 -1.90625 0.765625q-0.765625 0.75 -0.84375 2.015625zm12.719482 4.296875l0.234375 1.453125q-0.6875 0.1406231 -1.234375 0.1406231q-0.890625 0 -1.390625 -0.2812481q-0.484375 -0.28125 -0.6875 -0.734375q-0.203125 -0.46875 -0.203125 -1.9375l0 -5.578125l-1.203125 0l0 -1.265625l1.203125 0l0 -2.390625l1.625 -0.984375l0 3.375l1.65625 0l0 1.265625l-1.65625 0l0 5.671875q0 0.6875 0.078125 0.890625q0.09375 0.203125 0.28125 0.328125q0.203125 0.109375 0.578125 0.109375q0.265625 0 0.71875 -0.0625zm7.179077 1.4687481l0 -8.406248l-1.453125 0l0 -1.265625l1.453125 0l0 -1.03125q0 -0.96875 0.171875 -1.453125q0.234375 -0.640625 0.828125 -1.03125q0.59375 -0.390625 1.671875 -0.390625q0.6875 0 1.53125 0.15625l-0.25 1.4375q-0.5 -0.09375 -0.953125 -0.09375q-0.75 0 -1.0625 0.328125q-0.3125 0.3125 -0.3125 1.1875l0 0.8906
 25l1.890625 0l0 1.265625l-1.890625 0l0 8.406248l-1.625 0zm4.7457886 0l0 -13.359373l1.640625 0l0 13.359373l-1.640625 0zm3.5823364 -4.843748q0 -2.6875 1.484375 -3.96875q1.25 -1.078125 3.046875 -1.078125q2.0 0 3.265625 1.3125q1.265625 1.296875 1.265625 3.609375q0 1.859375 -0.5625 2.9375q-0.5625 1.0625 -1.640625 1.65625q-1.0625 0.5937481 -2.328125 0.5937481q-2.03125 0 -3.28125 -1.2968731q-1.25 -1.3125 -1.25 -3.765625zm1.6875 0q0 1.859375 0.796875 2.796875q0.8125 0.921875 2.046875 0.921875q1.21875 0 2.03125 -0.921875q0.8125 -0.9375 0.8125 -2.84375q0 -1.796875 -0.8125 -2.71875q-0.8125 -0.921875 -2.03125 -0.921875q-1.234375 0 -2.046875 0.921875q-0.796875 0.90625 -0.796875 2.765625zm11.078857 4.843748l-2.96875 -9.671873l1.703125 0l1.53125 5.578125l0.578125 2.078125q0.046875 -0.15625 0.5 -2.0l1.546875 -5.65625l1.6875 0l1.4375 5.609375l0.484375 1.84375l0.5625 -1.859375l1.65625 -5.59375l1.59375 0l-3.03125 9.671873l-1.703125 0l-1.53125 -5.796873l-0.375 -1.640625l-1.953125 7.437498l-1.71
 875 0z"
+       fill-rule="nonzero"
+       id="path999" />
+  </g>
+</svg>
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index fb250abf5..a98b16f0e 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -57,6 +57,7 @@ Programmer's Guide
     metrics_lib
     bpf_lib
     ipsec_lib
+    graph_lib
     source_org
     dev_kit_build_system
     dev_kit_root_make_help
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index 000bbf501..d208547ec 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -62,6 +62,30 @@ New Features
 
   * Added support for matching on IPv4 Time To Live and IPv6 Hop Limit.
 
+* **Added rte_graph library.**
+
+  Graph architecture abstracts the data processing functions as a ``node`` and
+  ``links`` them together to create a complex ``graph`` to enable reusable/modular
+  data processing functions. The graph library provides API to enable graph
+  framework operations such as create, lookup, dump and destroy on graph and node
+  operations such as clone, edge update, and edge shrink, etc.
+  The API also allows to create the stats cluster to monitor per graph and per node stats.
+
+* **Added rte_node library which consists of a set of packet processing nodes.**
+
+  The rte_node library that consists of nodes used by rte_graph library. Each
+  node performs a specific packet processing function based on application
+  configuration. The following nodes are added:
+
+  * Null node: Skeleton node that defines the general structure of a node.
+  * Ethernet device node: Consists of ethernet Rx/Tx nodes as well as ethernet
+    control APIs.
+  * IPv4 lookup node: Consists of ipv4 extract and lpm lookup node. Routes can
+    be configured by the application through ``rte_node_ip4_route_add`` function.
+  * IPv4 rewrite node: Consists of ipv4 and ethernet header rewrite functionality
+    that can be configured through ``rte_node_ip4_rewrite_add`` function.
+  * Packet drop node: Frees the packets received to their respective mempool.
+
 
 Removed Items
 -------------
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* [dpdk-dev] [PATCH v5 29/29] doc: add l3fwd graph application user guide
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (27 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 28/29] doc: add graph library programmer's guide guide jerinj
@ 2020-04-11 14:14         ` jerinj
  2020-04-30  8:07         ` [dpdk-dev] [PATCH v5 00/29] graph: introduce graph subsystem Tom Barbette
  2020-05-05 21:44         ` Thomas Monjalon
  30 siblings, 0 replies; 219+ messages in thread
From: jerinj @ 2020-04-11 14:14 UTC (permalink / raw)
  To: Thomas Monjalon, John McNamara, Marko Kovacevic, Ori Kam,
	Bruce Richardson, Radu Nicolau, Akhil Goyal, Tomasz Kantecki,
	Sunil Kumar Kori, Pavan Nikhilesh, Nithin Dabilpuram
  Cc: dev, david.marchand, mdr, mattias.ronnblom, kirankumark,
	xiao.w.wang, amo

From: Nithin Dabilpuram <ndabilpuram@marvell.com>

Adding the user guide for l3fwd graph application.

Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 MAINTAINERS                                   |   1 +
 doc/guides/rel_notes/release_20_05.rst        |   8 +
 doc/guides/sample_app_ug/index.rst            |   1 +
 doc/guides/sample_app_ug/intro.rst            |   4 +
 doc/guides/sample_app_ug/l3_forward_graph.rst | 334 ++++++++++++++++++
 5 files changed, 348 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index de57f452b..870cd8cae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1576,6 +1576,7 @@ F: doc/guides/sample_app_ug/l3_forward.rst
 
 M: Nithin Dabilpuram <ndabilpuram@marvell.com>
 F: examples/l3fwd-graph/
+F: doc/guides/sample_app_ug/l3_forward_graph.rst
 
 F: examples/link_status_interrupt/
 F: doc/guides/sample_app_ug/link_status_intr.rst
diff --git a/doc/guides/rel_notes/release_20_05.rst b/doc/guides/rel_notes/release_20_05.rst
index d208547ec..9d3e7bd69 100644
--- a/doc/guides/rel_notes/release_20_05.rst
+++ b/doc/guides/rel_notes/release_20_05.rst
@@ -86,6 +86,14 @@ New Features
     that can be configured through ``rte_node_ip4_rewrite_add`` function.
   * Packet drop node: Frees the packets received to their respective mempool.
 
+* **Added new l3fwd-graph sample application.**
+
+  Added an example application ``l3fwd-graph``. It demonstrates the usage of graph
+  library and node library for packet processing. In addition to the library usage
+  demonstration, this application can use for performance comparison with existing
+  ``l3fwd`` (The static code without any nodes) with the modular ``l3fwd-graph``
+  approach.
+
 
 Removed Items
 -------------
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index ac3445147..cf9c1e44d 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -29,6 +29,7 @@ Sample Applications User Guides
     l2_forward_event
     l2_forward_cat
     l3_forward
+    l3_forward_graph
     l3_forward_power_man
     l3_forward_access_ctrl
     link_status_intr
diff --git a/doc/guides/sample_app_ug/intro.rst b/doc/guides/sample_app_ug/intro.rst
index 6cd0342a1..8ff223b16 100644
--- a/doc/guides/sample_app_ug/intro.rst
+++ b/doc/guides/sample_app_ug/intro.rst
@@ -54,6 +54,10 @@ examples are highlighted below.
   forwarding, or ``l3fwd`` application does forwarding based on Internet
   Protocol, IPv4 or IPv6 like a simple router.
 
+* :doc:`Network Layer 3 forwarding Graph<l3_forward_graph>`: The Network Layer3
+  forwarding Graph, or ``l3fwd_graph`` application does forwarding based on IPv4
+  like a simple router with DPDK Graph framework.
+
 * :doc:`Hardware packet copying<ioat>`: The Hardware packet copying,
   or ``ioatfwd`` application demonstrates how to use IOAT rawdev driver for
   copying packets between two threads.
diff --git a/doc/guides/sample_app_ug/l3_forward_graph.rst b/doc/guides/sample_app_ug/l3_forward_graph.rst
new file mode 100644
index 000000000..df50827ba
--- /dev/null
+++ b/doc/guides/sample_app_ug/l3_forward_graph.rst
@@ -0,0 +1,334 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(C) 2020 Marvell International Ltd.
+
+L3 Forwarding Graph Sample Application
+======================================
+
+The L3 Forwarding Graph application is a simple example of packet processing
+using the DPDK Graph framework. The application performs L3 forwarding using
+Graph framework and nodes written for graph framework.
+
+Overview
+--------
+
+The application demonstrates the use of the graph framework and graph nodes
+``ethdev_rx``, ``ip4_lookup``, ``ip4_rewrite``, ``ethdev_tx`` and ``pkt_drop`` in DPDK to
+implement packet forwarding.
+
+The initialization is very similar to those of the :doc:`l3_forward`.
+There is also additional initialization of graph for graph object creation
+and configuration per lcore.
+Run-time path is main thing that differs from L3 forwarding sample application.
+Difference is that forwarding logic starting from Rx, followed by LPM lookup,
+TTL update and finally Tx is implemented inside graph nodes. These nodes are
+interconnected in graph framework. Application main loop needs to walk over
+graph using ``rte_graph_walk()`` with graph objects created one per slave lcore.
+
+The lookup method is as per implementation of ``ip4_lookup`` graph node.
+The ID of the output interface for the input packet is the next hop returned by
+the LPM lookup. The set of LPM rules used by the application is statically
+configured and provided to ``ip4_lookup`` graph node and ``ip4_rewrite`` graph node
+using node control API ``rte_node_ip4_route_add()`` and ``rte_node_ip4_rewrite_add()``.
+
+In the sample application, only IPv4 forwarding is supported as of now.
+
+Compiling the Application
+-------------------------
+
+To compile the sample application see :doc:`compiling`.
+
+The application is located in the ``l3fwd-graph`` sub-directory.
+
+Running the Application
+-----------------------
+
+The application has a number of command line options similar to l3fwd::
+
+    ./l3fwd-graph [EAL options] -- -p PORTMASK
+                                   [-P]
+                                   --config(port,queue,lcore)[,(port,queue,lcore)]
+                                   [--eth-dest=X,MM:MM:MM:MM:MM:MM]
+                                   [--enable-jumbo [--max-pkt-len PKTLEN]]
+                                   [--no-numa]
+                                   [--per-port-pool]
+
+Where,
+
+* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
+
+* ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
+  Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
+
+* ``--config (port,queue,lcore)[,(port,queue,lcore)]:`` Determines which queues from which ports are mapped to which cores.
+
+* ``--eth-dest=X,MM:MM:MM:MM:MM:MM:`` Optional, ethernet destination for port X.
+
+* ``--enable-jumbo:`` Optional, enables jumbo frames.
+
+* ``--max-pkt-len:`` Optional, under the premise of enabling jumbo, maximum packet length in decimal (64-9600).
+
+* ``--no-numa:`` Optional, disables numa awareness.
+
+* ``--per-port-pool:`` Optional, set to use independent buffer pools per port. Without this option, single buffer pool is used for all ports.
+
+For example, consider a dual processor socket platform with 8 physical cores, where cores 0-7 and 16-23 appear on socket 0,
+while cores 8-15 and 24-31 appear on socket 1.
+
+To enable L3 forwarding between two ports, assuming that both ports are in the same socket, using two cores, cores 1 and 2,
+(which are in the same socket too), use the following command:
+
+.. code-block:: console
+
+    ./build/l3fwd-graph -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+
+In this command:
+
+*   The -l option enables cores 1, 2
+
+*   The -p option enables ports 0 and 1
+
+*   The --config option enables one queue on each port and maps each (port,queue) pair to a specific core.
+    The following table shows the mapping in this example:
+
++----------+-----------+-----------+-------------------------------------+
+| **Port** | **Queue** | **lcore** | **Description**                     |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 0        | 0         | 1         | Map queue 0 from port 0 to lcore 1. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+| 1        | 0         | 2         | Map queue 0 from port 1 to lcore 2. |
+|          |           |           |                                     |
++----------+-----------+-----------+-------------------------------------+
+
+Refer to the *DPDK Getting Started Guide* for general information on running applications and
+the Environment Abstraction Layer (EAL) options.
+
+.. _l3_fwd_graph_explanation:
+
+Explanation
+-----------
+
+The following sections provide some explanation of the sample application code.
+As mentioned in the overview section, the initialization is similar to that of
+the :doc:`l3_forward`. Run-time path though similar in functionality to that of
+:doc:`l3_forward`, major part of the implementation is in graph nodes via used
+via ``librte_node`` library.
+The following sections describe aspects that are specific to the L3 Forwarding
+Graph sample application.
+
+Graph Node Pre-Init Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After device configuration and device Rx, Tx queue setup is complete,
+a minimal config of port id, num_rx_queues, num_tx_queues, mempools etc will
+be passed to *ethdev_** node ctrl API ``rte_node_eth_config()``. This will be
+lead to the clone of ``ethdev_rx`` and ``ethdev_tx`` nodes as ``ethdev_rx-X-Y`` and
+``ethdev_tx-X`` where X, Y represent port id and queue id associated with them.
+In case of ``ethdev_tx-X`` nodes, tx queue id assigned per instance of the node
+is same as graph id.
+
+These cloned nodes along with existing static nodes such as ``ip4_lookup`` and
+``ip4_rewrite`` will be used in graph creation to associate node's to lcore
+specific graph object.
+
+.. code-block:: c
+
+    RTE_ETH_FOREACH_DEV(portid)
+    {
+
+        /* ... */
+        ret = rte_eth_dev_configure(portid, nb_rx_queue,
+                                    n_tx_queue, &local_port_conf);
+        /* ... */
+
+        /* Init one TX queue per couple (lcore,port) */
+        queueid = 0;
+        for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+            /* ... */
+            ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
+                                         socketid, txconf);
+            /* ... */
+            queueid++;
+        }
+
+        /* Setup ethdev node config */
+        ethdev_conf[nb_conf].port_id = portid;
+        ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue;
+        ethdev_conf[nb_conf].num_tx_queues = n_tx_queue;
+        if (!per_port_pool)
+            ethdev_conf[nb_conf].mp = pktmbuf_pool[0];
+        else
+          ethdev_conf[nb_conf].mp = pktmbuf_pool[portid];
+        ethdev_conf[nb_conf].mp_count = NB_SOCKETS;
+
+        nb_conf++;
+        printf("\n");
+    }
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* Init RX queues */
+        for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+            /* ... */
+            if (!per_port_pool)
+                ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                             &rxq_conf, pktmbuf_pool[0][socketid]);
+            else
+              ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd, socketid,
+                                           &rxq_conf, pktmbuf_pool[portid][socketid]);
+            /* ... */
+        }
+    }
+
+    /* Ethdev node config, skip rx queue mapping */
+    ret = rte_node_eth_config(ethdev_conf, nb_conf, nb_graphs);
+
+Graph Initialization
+~~~~~~~~~~~~~~~~~~~~
+
+Now a graph needs to be created with a specific set of nodes for every lcore.
+A graph object returned after graph creation is a per lcore object and
+cannot be shared between lcores. Since ``ethdev_tx-X`` node is per port node,
+it can be associated with all the graphs created as all the lcores should have
+Tx capability for every port. But ``ethdev_rx-X-Y`` node is created per
+(port, rx_queue_id), so they should be associated with a graph based on
+the application argument ``--config`` specifying rx queue mapping to lcore.
+
+.. note::
+
+    The Graph creation will fail if the passed set of shell node pattern's
+    are not sufficient to meet their inter-dependency or even one node is not
+    found with a given regex node pattern.
+
+.. code-block:: c
+
+    static const char *const default_patterns[] = {
+        "ip4*",
+        "ethdev_tx-*",
+        "pkt_drop",
+    };
+    const char **node_patterns;
+    uint16_t nb_pattern;
+
+    /* ... */
+
+    /* Create a graph object per lcore with common nodes and
+     * lcore specific nodes based on application arguments
+     */
+    nb_patterns = RTE_DIM(default_patterns);
+    node_patterns = malloc((MAX_RX_QUEUE_PER_LCORE + nb_patterns) *
+                           sizeof(*node_patterns));
+    memcpy(node_patterns, default_patterns,
+           nb_patterns * sizeof(*node_patterns));
+
+    memset(&graph_conf, 0, sizeof(graph_conf));
+
+    /* Common set of nodes in every lcore's graph object */
+    graph_conf.node_patterns = node_patterns;
+
+    for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+        /* ... */
+
+        /* Skip graph creation if no source exists */
+        if (!qconf->n_rx_queue)
+            continue;
+
+        /* Add rx node patterns of this lcore based on --config */
+        for (i = 0; i < qconf->n_rx_queue; i++) {
+            graph_conf.node_patterns[nb_patterns + i] =
+                                qconf->rx_queue_list[i].node_name;
+        }
+
+        graph_conf.nb_node_patterns = nb_patterns + i;
+        graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id);
+
+        snprintf(qconf->name, sizeof(qconf->name), "worker_%u", lcore_id);
+
+        graph_id = rte_graph_create(qconf->name, &graph_conf);
+
+        /* ... */
+
+        qconf->graph = rte_graph_lookup(qconf->name);
+
+        /* ... */
+    }
+
+Forwarding data(Route, Next-Hop) addition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once graph objects are created, node specific info like routes and rewrite
+headers will be provided run-time using ``rte_node_ip4_route_add()`` and
+``rte_node_ip4_rewrite_add()`` API.
+
+.. note::
+
+    Since currently ``ip4_lookup`` and ``ip4_rewrite`` nodes don't support
+    lock-less mechanisms(RCU, etc) to add run-time forwarding data like route and
+    rewrite data, forwarding data is added before packet processing loop is
+    launched on slave lcore.
+
+.. code-block:: c
+
+    /* Add route to ip4 graph infra */
+    for (i = 0; i < IPV4_L3FWD_LPM_NUM_ROUTES; i++) {
+        /* ... */
+
+        dst_port = ipv4_l3fwd_lpm_route_array[i].if_out;
+        next_hop = i;
+
+        /* ... */
+        ret = rte_node_ip4_route_add(ipv4_l3fwd_lpm_route_array[i].ip,
+                                     ipv4_l3fwd_lpm_route_array[i].depth, next_hop,
+                                     RTE_NODE_IP4_LOOKUP_NEXT_REWRITE);
+
+        /* ... */
+
+        memcpy(rewrite_data, val_eth + dst_port, rewrite_len);
+
+        /* Add next hop for a given destination */
+        ret = rte_node_ip4_rewrite_add(next_hop, rewrite_data,
+                                       rewrite_len, dst_port);
+
+        RTE_LOG(INFO, L3FWD_GRAPH, "Added route %s, next_hop %u\n",
+                route_str, next_hop);
+    }
+
+Packet Forwarding using Graph Walk
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that all the device configurations are done, graph creations are done and
+forwarding data is updated with nodes, slave lcores will be launched with graph
+main loop. Graph main loop is very simple in the sense that it needs to
+continuously call a non-blocking API ``rte_graph_walk()`` with it's lcore
+specific graph object that was already created.
+
+.. note::
+
+    rte_graph_walk() will walk over all the sources nodes i.e ``ethdev_rx-X-Y``
+    associated with a given graph and Rx the available packets and enqueue them
+    to the following node ``ip4_lookup`` which then will enqueue them to ``ip4_rewrite``
+    node if LPM lookup succeeds. ``ip4_rewrite`` node then will update Ethernet header
+    as per next-hop data and transmit the packet via port 'Z' by enqueuing
+    to ``ethdev_tx-Z`` node instance in its graph object.
+
+.. code-block:: c
+
+    /* Main processing loop */
+    static int
+    graph_main_loop(void *conf)
+    {
+        // ...
+
+        lcore_id = rte_lcore_id();
+        qconf = &lcore_conf[lcore_id];
+        graph = qconf->graph;
+
+        RTE_LOG(INFO, L3FWD_GRAPH,
+                "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id,
+                qconf->name, graph);
+
+        /* Walk over graph until signal to quit */
+        while (likely(!force_quit))
+            rte_graph_walk(graph);
+        return 0;
+    }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v5 00/29] graph: introduce graph subsystem
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (28 preceding siblings ...)
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 29/29] doc: add l3fwd graph application user guide jerinj
@ 2020-04-30  8:07         ` Tom Barbette
  2020-04-30  8:42           ` Jerin Jacob
  2020-05-05 21:44         ` Thomas Monjalon
  30 siblings, 1 reply; 219+ messages in thread
From: Tom Barbette @ 2020-04-30  8:07 UTC (permalink / raw)
  To: jerinj
  Cc: dev, thomas, david.marchand, mdr, mattias.ronnblom, kirankumark,
	pbhagavatula, ndabilpuram, xiao.w.wang, amo

Hi all,

I could not check all discussions regarding the graph subsystem, but I 
could not find a trivia behind the idea of re-creating yet another graph 
processing system like VPP, BESS, Click/FastClick and a few others that 
all support DPDK already and comes with up to thousands of "nodes" 
already built?

Is there something fundamentally better than those? Or this is just to 
provide a clean in-house API?

Thanks,

Tom

Le 11/04/2020 à 16:13, jerinj@marvell.com a écrit :
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Using graph traversal for packet processing is a proven architecture
> that has been implemented in various open source libraries.
> 
> Graph architecture for packet processing enables abstracting the data
> processing functions as “nodes” and “links” them together to create a
> complex “graph” to create reusable/modular data processing functions.
> 
> The patchset further includes performance enhancements and modularity
> to the DPDK as discussed in more detail below.
> 
> v5..v4:
> ------
> Addressed the following review comments from Andrzej Ostruszka.
> 
> 1) Addressed and comment in (http://mails.dpdk.org/archives/dev/2020-April/162184.html)
> and improved following function prototypes/return types and adjusted the
> implementation
> a) rte_graph_node_get
> b) rte_graph_max_count
> c) rte_graph_export
> d) rte_graph_destroy
> 2) Updated UT and l3fwd-graph for updated function prototype
> 3) bug fix in edge_update
> 4) avoid reading graph_src_nodes_count() twice in rte_graph_create()
> 5) Fix graph_mem_fixup_secondray typo
> 6) Fixed Doxygen comments for rte_node_next_stream_put
> 7) Updated the documentation to reflect the same.
> 8) Removed RTE prefix from rte_node_mbuf_priv[1|2] * as they are
> internal defines
> 9) Limited next_hop id provided to LPM route add in
> librte_node/ip4_lookup.c to 24 bits ()
> 10) Fixed pattern array overflow issue with l3fwd-graph/main.c by
> splitting pattern
> array to default + non-default array. Updated doc with the same info.
> 11) Fixed parsing issues in parse_config() in l3fwd-graph/main.c inline
> with issues reported
> in l2fwd-event
> 12)Removed next_hop field in l3fwd-graph/main.c main()
> 13) Fixed graph create error check in l3fwd-graph/main.c main()
> 
> v4..v3:
> -------
> Addressed the following review comments from Wang, Xiao W
> 
> 1) Remove unnecessary line from rte_graph.h
> 2) Fix a typo from rte_graph.h
> 3) Move NODE_ID_CHECK to 3rd patch where it is first used.
> 4) Fixed bug in edge_update()
> 
> v3..v2:
> -------
> 1) refactor ipv4 node lookup by moving SSE and NEON specific code to
> lib/librte_node/ip4_lookup_sse.h and lib/librte_node/ip4_lookup_neon.h
> 2) Add scalar version of process() function for ipv4 lookup to make
> the node work on NON x86 and arm64 machines.
> 
> v2..v1:
> ------
> 1) Added programmer guide/implementation documentation and l3fwd-graph doc
> 
> RFC..v1:
> --------
> 
> 1) Split the patch to more logical ones for review.
> 2) Added doxygen comments for the API
> 3) Code cleanup
> 4) Additional performance improvements.
> Delta between l3fwd and l3fwd-graph is negligible now.
> (~1%) on octeontx2.
> 5) Added SIMD routines for x86 in additional to arm64.
> 
> Hosted in netlify for easy reference:
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> Programmer’s Guide:
> https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html
> 
> l3fwd-graph doc:
> https://dpdk-graph.netlify.com/doc/html/guides/sample_app_ug/l3_forward_graph.html
> 
> API doc:
> https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html
> https://dpdk-graph.netlify.com/doc/html/api/rte__graph__worker_8h.html
> https://dpdk-graph.netlify.com/doc/html/api/rte__node__eth__api_8h.html
> https://dpdk-graph.netlify.com/doc/html/api/rte__node__ip4__api_8h.html
> 
> 2) Added the release notes for the this feature
> 
> 3) Fix build issues reported by CI for v1:
> http://mails.dpdk.org/archives/test-report/2020-March/121326.html
> 
> 
> Addional nodes planned for v20.08
> ----------------------------------
> 1) Packet classification node
> 2) Support for IPV6 LPM node
> 
> 
> This patchset contains
> -----------------------------
> 1) The API definition to "create" nodes and "link" together to create a
> "graph" for packet processing. See, lib/librte_graph/rte_graph.h
> 
> 2) The Fast path API definition for the graph walker and enqueue
> function used by the workers. See, lib/librte_graph/rte_graph_worker.h
> 
> 3) Optimized SW implementation for (1) and (2). See, lib/librte_graph/
> 
> 4) Test case to verify the graph infrastructure functionality
> See, app/test/test_graph.c
>   
> 5) Performance test cases to evaluate the cost of graph walker and nodes
> enqueue fast-path function for various combinations.
> 
> See app/test/test_graph_perf.c
> 
> 6) Packet processing nodes(Null, Rx, Tx, Pkt drop, IPV4 rewrite, IPv4
> lookup)
> using graph infrastructure. See lib/librte_node/*
> 
> 7) An example application to showcase l3fwd
> (functionality same as existing examples/l3fwd) using graph
> infrastructure and use packets processing nodes (item (6)). See examples/l3fwd-graph/.
> 
> Performance
> -----------
> 1) Graph walk and node enqueue overhead can be tested with performance
> test case application [1]
> # If all packets go from a node to another node (we call it as
> # "homerun") then it will be just a pointer swap for a burst of packets.
> # In the worst case, a couple of handful cycles to move an object from a
> node to another node.
> 
> 2) Performance comparison with existing l3fwd (The complete static code
> with out any nodes) vs modular l3fwd-graph with 5 nodes
> (ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
> Here is graphical representation of the l3fwd-graph as Graphviz dot
> file:
> http://bit.ly/39UPPGm
> 
> # l3fwd-graph performance is -1.2% wrt static l3fwd.
> 
> # We have simulated the similar test with existing librte_pipeline
> # application [4].
> ip_pipline application is -48.62% wrt static l3fwd.
> 
> The above results are on octeontx2. It may vary on other platforms.
> The platforms with higher L1 and L2 caches will have further better
> performance.
> 
> 
> Tested architectures:
> --------------------
> 1) AArch64
> 2) X86
> 
> 
> Identified tweaking for better performance on different targets
> ---------------------------------------------------------------
> 1) Test with various burst size values (256, 128, 64, 32) using
> CONFIG_RTE_GRAPH_BURST_SIZE config option.
> Based on our testing, on x86 and arm64 servers, The sweet spot is 256
> burst size.
> While on arm64 embedded SoCs, it is either 64 or 128.
> 
> 2) Disable node statistics (use CONFIG_RTE_LIBRTE_GRAPH_STATS config
> option)
> if not needed.
> 
> 3) Use arm64 optimized memory copy for arm64 architecture by
> selecting CONFIG_RTE_ARCH_ARM64_MEMCPY.
> 
> Commands to run tests
> ---------------------
> 
> [1]
> perf test:
> echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30
> 
> [2]
> functionality test:
> echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30
> 
> [3]
> l3fwd-graph:
> ./l3fwd-graph -c 0x100  -- -p 0x3 --config="(0, 0, 8)" -P
> 
> [4]
> # ./ip_pipeline --c 0xff0000 -- -s route.cli
> 
> Route.cli: (Copy paste to the shell to avoid dos format issues)
> 
> https://pastebin.com/raw/B4Ktx7TT
> 
> Jerin Jacob (13):
>    graph: define the public API for graph support
>    graph: implement node registration
>    graph: implement node operations
>    graph: implement node debug routines
>    graph: implement internal graph operation helpers
>    graph: populate fastpath memory for graph reel
>    graph: implement create and destroy APIs
>    graph: implement graph operation APIs
>    graph: implement Graphviz export
>    graph: implement debug routines
>    graph: implement stats support
>    graph: implement fastpath API routines
>    doc: add graph library programmer's guide guide
> 
> Kiran Kumar K (2):
>    graph: add unit test case
>    node: add ipv4 rewrite node
> 
> Nithin Dabilpuram (11):
>    node: add log infra and null node
>    node: add ethdev Rx node
>    node: add ethdev Tx node
>    node: add ethdev Rx and Tx node ctrl API
>    node: ipv4 lookup for arm64
>    node: add ipv4 rewrite and lookup ctrl API
>    node: add packet drop node
>    l3fwd-graph: add graph based l3fwd skeleton
>    l3fwd-graph: add ethdev configuration changes
>    l3fwd-graph: add graph config and main loop
>    doc: add l3fwd graph application user guide
> 
> Pavan Nikhilesh (3):
>    graph: add performance testcase
>    node: add generic ipv4 lookup node
>    node: ipv4 lookup for x86
> 
>   MAINTAINERS                                   |   14 +
>   app/test/Makefile                             |    7 +
>   app/test/meson.build                          |   12 +-
>   app/test/test_graph.c                         |  819 ++++
>   app/test/test_graph_perf.c                    | 1057 ++++++
>   config/common_base                            |   12 +
>   config/rte_config.h                           |    4 +
>   doc/api/doxy-api-index.md                     |    5 +
>   doc/api/doxy-api.conf.in                      |    2 +
>   doc/guides/prog_guide/graph_lib.rst           |  397 ++
>   .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
>   .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
>   doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
>   doc/guides/prog_guide/index.rst               |    1 +
>   doc/guides/rel_notes/release_20_05.rst        |   32 +
>   doc/guides/sample_app_ug/index.rst            |    1 +
>   doc/guides/sample_app_ug/intro.rst            |    4 +
>   doc/guides/sample_app_ug/l3_forward_graph.rst |  334 ++
>   examples/Makefile                             |    3 +
>   examples/l3fwd-graph/Makefile                 |   58 +
>   examples/l3fwd-graph/main.c                   | 1126 ++++++
>   examples/l3fwd-graph/meson.build              |   13 +
>   examples/meson.build                          |    6 +-
>   lib/Makefile                                  |    6 +
>   lib/librte_graph/Makefile                     |   28 +
>   lib/librte_graph/graph.c                      |  587 +++
>   lib/librte_graph/graph_debug.c                |   84 +
>   lib/librte_graph/graph_ops.c                  |  169 +
>   lib/librte_graph/graph_populate.c             |  234 ++
>   lib/librte_graph/graph_private.h              |  347 ++
>   lib/librte_graph/graph_stats.c                |  406 ++
>   lib/librte_graph/meson.build                  |   11 +
>   lib/librte_graph/node.c                       |  421 +++
>   lib/librte_graph/rte_graph.h                  |  668 ++++
>   lib/librte_graph/rte_graph_version.map        |   47 +
>   lib/librte_graph/rte_graph_worker.h           |  510 +++
>   lib/librte_node/Makefile                      |   32 +
>   lib/librte_node/ethdev_ctrl.c                 |  115 +
>   lib/librte_node/ethdev_rx.c                   |  221 ++
>   lib/librte_node/ethdev_rx_priv.h              |   81 +
>   lib/librte_node/ethdev_tx.c                   |   86 +
>   lib/librte_node/ethdev_tx_priv.h              |   62 +
>   lib/librte_node/ip4_lookup.c                  |  215 ++
>   lib/librte_node/ip4_lookup_neon.h             |  238 ++
>   lib/librte_node/ip4_lookup_sse.h              |  244 ++
>   lib/librte_node/ip4_rewrite.c                 |  326 ++
>   lib/librte_node/ip4_rewrite_priv.h            |   77 +
>   lib/librte_node/log.c                         |   14 +
>   lib/librte_node/meson.build                   |   10 +
>   lib/librte_node/node_private.h                |   79 +
>   lib/librte_node/null.c                        |   23 +
>   lib/librte_node/pkt_drop.c                    |   26 +
>   lib/librte_node/rte_node_eth_api.h            |   64 +
>   lib/librte_node/rte_node_ip4_api.h            |   78 +
>   lib/librte_node/rte_node_version.map          |    9 +
>   lib/meson.build                               |    5 +-
>   meson.build                                   |    1 +
>   mk/rte.app.mk                                 |    2 +
>   58 files changed, 14538 insertions(+), 5 deletions(-)
>   create mode 100644 app/test/test_graph.c
>   create mode 100644 app/test/test_graph_perf.c
>   create mode 100644 doc/guides/prog_guide/graph_lib.rst
>   create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
>   create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
>   create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg
>   create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst
>   create mode 100644 examples/l3fwd-graph/Makefile
>   create mode 100644 examples/l3fwd-graph/main.c
>   create mode 100644 examples/l3fwd-graph/meson.build
>   create mode 100644 lib/librte_graph/Makefile
>   create mode 100644 lib/librte_graph/graph.c
>   create mode 100644 lib/librte_graph/graph_debug.c
>   create mode 100644 lib/librte_graph/graph_ops.c
>   create mode 100644 lib/librte_graph/graph_populate.c
>   create mode 100644 lib/librte_graph/graph_private.h
>   create mode 100644 lib/librte_graph/graph_stats.c
>   create mode 100644 lib/librte_graph/meson.build
>   create mode 100644 lib/librte_graph/node.c
>   create mode 100644 lib/librte_graph/rte_graph.h
>   create mode 100644 lib/librte_graph/rte_graph_version.map
>   create mode 100644 lib/librte_graph/rte_graph_worker.h
>   create mode 100644 lib/librte_node/Makefile
>   create mode 100644 lib/librte_node/ethdev_ctrl.c
>   create mode 100644 lib/librte_node/ethdev_rx.c
>   create mode 100644 lib/librte_node/ethdev_rx_priv.h
>   create mode 100644 lib/librte_node/ethdev_tx.c
>   create mode 100644 lib/librte_node/ethdev_tx_priv.h
>   create mode 100644 lib/librte_node/ip4_lookup.c
>   create mode 100644 lib/librte_node/ip4_lookup_neon.h
>   create mode 100644 lib/librte_node/ip4_lookup_sse.h
>   create mode 100644 lib/librte_node/ip4_rewrite.c
>   create mode 100644 lib/librte_node/ip4_rewrite_priv.h
>   create mode 100644 lib/librte_node/log.c
>   create mode 100644 lib/librte_node/meson.build
>   create mode 100644 lib/librte_node/node_private.h
>   create mode 100644 lib/librte_node/null.c
>   create mode 100644 lib/librte_node/pkt_drop.c
>   create mode 100644 lib/librte_node/rte_node_eth_api.h
>   create mode 100644 lib/librte_node/rte_node_ip4_api.h
>   create mode 100644 lib/librte_node/rte_node_version.map
> 

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v5 00/29] graph: introduce graph subsystem
  2020-04-30  8:07         ` [dpdk-dev] [PATCH v5 00/29] graph: introduce graph subsystem Tom Barbette
@ 2020-04-30  8:42           ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-04-30  8:42 UTC (permalink / raw)
  To: Tom Barbette
  Cc: Jerin Jacob, dpdk-dev, Thomas Monjalon, David Marchand,
	Ray Kinsella, Mattias Rönnblom, Kiran Kumar K,
	Pavan Nikhilesh, Nithin Dabilpuram, Xiao Wang, Andrzej Ostruszka

On Thu, Apr 30, 2020 at 1:38 PM Tom Barbette <barbette@kth.se> wrote:
>
> Hi all,
>
> I could not check all discussions regarding the graph subsystem, but I
> could not find a trivia behind the idea of re-creating yet another graph
> processing system like VPP, BESS, Click/FastClick and a few others that
> all support DPDK already and comes with up to thousands of "nodes"
> already built?
>
> Is there something fundamentally better than those? Or this is just to
> provide a clean in-house API?

Enumerating some of the features:

0) Native mbuf based nodes. This will avoid, mbuf to other data plane
container conversion cost in fastpath.
1) Nodes as plugins.
2) Support for out of tree nodes.
3) Inbuilt nodes for packet processing.
4) Multi-process support.
5) Low overhead graph walk and node enqueue.[1] See performance data
from the cover letter.
6) Low overhead statistics collection infrastructure.
7) Support to export the graph as a Graphviz dot file. See rte_graph_export().
8) Allow having another graph walk implementation in the future by
segregating the fast path(rte_graph_worker.h) and slow path code.
9) Native DPDK apps for DPDK drivers to leverage graph architecture.

I think item (0), (4), (5), (9) useful for the DPDK ecosystem.

Performance[1]
-----------
1) Graph walk and node enqueue overhead can be tested with performance
test case application [1]
# If all packets go from a node to another node (we call it as
# "homerun") then it will be just a pointer swap for a burst of packets.
# In the worst case, a couple of handful cycles to move an object from a
node to another node.

2) Performance comparison with existing l3fwd (The complete static code
with out any nodes) vs modular l3fwd-graph with 5 nodes
(ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
Here is graphical representation of the l3fwd-graph as Graphviz dot
file:
http://bit.ly/39UPPGm

# l3fwd-graph performance is -1.2% wrt static l3fwd.

# We have simulated the similar test with existing librte_pipeline
# application [4].
ip_pipline application is -48.62% wrt static l3fwd.

The above results are on octeontx2. It may vary on other platforms.
The platforms with higher L1 and L2 caches will have further better
performance.
>
> Thanks,
>
> Tom
>
> Le 11/04/2020 à 16:13, jerinj@marvell.com a écrit :
> > From: Jerin Jacob <jerinj@marvell.com>
> >
> > Using graph traversal for packet processing is a proven architecture
> > that has been implemented in various open source libraries.
> >
> > Graph architecture for packet processing enables abstracting the data
> > processing functions as “nodes” and “links” them together to create a
> > complex “graph” to create reusable/modular data processing functions.
> >
> > The patchset further includes performance enhancements and modularity
> > to the DPDK as discussed in more detail below.
> >
> > v5..v4:
> > ------
> > Addressed the following review comments from Andrzej Ostruszka.
> >
> > 1) Addressed and comment in (http://mails.dpdk.org/archives/dev/2020-April/162184.html)
> > and improved following function prototypes/return types and adjusted the
> > implementation
> > a) rte_graph_node_get
> > b) rte_graph_max_count
> > c) rte_graph_export
> > d) rte_graph_destroy
> > 2) Updated UT and l3fwd-graph for updated function prototype
> > 3) bug fix in edge_update
> > 4) avoid reading graph_src_nodes_count() twice in rte_graph_create()
> > 5) Fix graph_mem_fixup_secondray typo
> > 6) Fixed Doxygen comments for rte_node_next_stream_put
> > 7) Updated the documentation to reflect the same.
> > 8) Removed RTE prefix from rte_node_mbuf_priv[1|2] * as they are
> > internal defines
> > 9) Limited next_hop id provided to LPM route add in
> > librte_node/ip4_lookup.c to 24 bits ()
> > 10) Fixed pattern array overflow issue with l3fwd-graph/main.c by
> > splitting pattern
> > array to default + non-default array. Updated doc with the same info.
> > 11) Fixed parsing issues in parse_config() in l3fwd-graph/main.c inline
> > with issues reported
> > in l2fwd-event
> > 12)Removed next_hop field in l3fwd-graph/main.c main()
> > 13) Fixed graph create error check in l3fwd-graph/main.c main()
> >
> > v4..v3:
> > -------
> > Addressed the following review comments from Wang, Xiao W
> >
> > 1) Remove unnecessary line from rte_graph.h
> > 2) Fix a typo from rte_graph.h
> > 3) Move NODE_ID_CHECK to 3rd patch where it is first used.
> > 4) Fixed bug in edge_update()
> >
> > v3..v2:
> > -------
> > 1) refactor ipv4 node lookup by moving SSE and NEON specific code to
> > lib/librte_node/ip4_lookup_sse.h and lib/librte_node/ip4_lookup_neon.h
> > 2) Add scalar version of process() function for ipv4 lookup to make
> > the node work on NON x86 and arm64 machines.
> >
> > v2..v1:
> > ------
> > 1) Added programmer guide/implementation documentation and l3fwd-graph doc
> >
> > RFC..v1:
> > --------
> >
> > 1) Split the patch to more logical ones for review.
> > 2) Added doxygen comments for the API
> > 3) Code cleanup
> > 4) Additional performance improvements.
> > Delta between l3fwd and l3fwd-graph is negligible now.
> > (~1%) on octeontx2.
> > 5) Added SIMD routines for x86 in additional to arm64.
> >
> > Hosted in netlify for easy reference:
> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > Programmer’s Guide:
> > https://dpdk-graph.netlify.com/doc/html/guides/prog_guide/graph_lib.html
> >
> > l3fwd-graph doc:
> > https://dpdk-graph.netlify.com/doc/html/guides/sample_app_ug/l3_forward_graph.html
> >
> > API doc:
> > https://dpdk-graph.netlify.com/doc/html/api/rte__graph_8h.html
> > https://dpdk-graph.netlify.com/doc/html/api/rte__graph__worker_8h.html
> > https://dpdk-graph.netlify.com/doc/html/api/rte__node__eth__api_8h.html
> > https://dpdk-graph.netlify.com/doc/html/api/rte__node__ip4__api_8h.html
> >
> > 2) Added the release notes for the this feature
> >
> > 3) Fix build issues reported by CI for v1:
> > http://mails.dpdk.org/archives/test-report/2020-March/121326.html
> >
> >
> > Addional nodes planned for v20.08
> > ----------------------------------
> > 1) Packet classification node
> > 2) Support for IPV6 LPM node
> >
> >
> > This patchset contains
> > -----------------------------
> > 1) The API definition to "create" nodes and "link" together to create a
> > "graph" for packet processing. See, lib/librte_graph/rte_graph.h
> >
> > 2) The Fast path API definition for the graph walker and enqueue
> > function used by the workers. See, lib/librte_graph/rte_graph_worker.h
> >
> > 3) Optimized SW implementation for (1) and (2). See, lib/librte_graph/
> >
> > 4) Test case to verify the graph infrastructure functionality
> > See, app/test/test_graph.c
> >
> > 5) Performance test cases to evaluate the cost of graph walker and nodes
> > enqueue fast-path function for various combinations.
> >
> > See app/test/test_graph_perf.c
> >
> > 6) Packet processing nodes(Null, Rx, Tx, Pkt drop, IPV4 rewrite, IPv4
> > lookup)
> > using graph infrastructure. See lib/librte_node/*
> >
> > 7) An example application to showcase l3fwd
> > (functionality same as existing examples/l3fwd) using graph
> > infrastructure and use packets processing nodes (item (6)). See examples/l3fwd-graph/.
> >
> > Performance
> > -----------
> > 1) Graph walk and node enqueue overhead can be tested with performance
> > test case application [1]
> > # If all packets go from a node to another node (we call it as
> > # "homerun") then it will be just a pointer swap for a burst of packets.
> > # In the worst case, a couple of handful cycles to move an object from a
> > node to another node.
> >
> > 2) Performance comparison with existing l3fwd (The complete static code
> > with out any nodes) vs modular l3fwd-graph with 5 nodes
> > (ip4_lookup, ip4_rewrite, ethdev_tx, ethdev_rx, pkt_drop).
> > Here is graphical representation of the l3fwd-graph as Graphviz dot
> > file:
> > http://bit.ly/39UPPGm
> >
> > # l3fwd-graph performance is -1.2% wrt static l3fwd.
> >
> > # We have simulated the similar test with existing librte_pipeline
> > # application [4].
> > ip_pipline application is -48.62% wrt static l3fwd.
> >
> > The above results are on octeontx2. It may vary on other platforms.
> > The platforms with higher L1 and L2 caches will have further better
> > performance.
> >
> >
> > Tested architectures:
> > --------------------
> > 1) AArch64
> > 2) X86
> >
> >
> > Identified tweaking for better performance on different targets
> > ---------------------------------------------------------------
> > 1) Test with various burst size values (256, 128, 64, 32) using
> > CONFIG_RTE_GRAPH_BURST_SIZE config option.
> > Based on our testing, on x86 and arm64 servers, The sweet spot is 256
> > burst size.
> > While on arm64 embedded SoCs, it is either 64 or 128.
> >
> > 2) Disable node statistics (use CONFIG_RTE_LIBRTE_GRAPH_STATS config
> > option)
> > if not needed.
> >
> > 3) Use arm64 optimized memory copy for arm64 architecture by
> > selecting CONFIG_RTE_ARCH_ARM64_MEMCPY.
> >
> > Commands to run tests
> > ---------------------
> >
> > [1]
> > perf test:
> > echo "graph_perf_autotest" | sudo ./build/app/test/dpdk-test -c 0x30
> >
> > [2]
> > functionality test:
> > echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30
> >
> > [3]
> > l3fwd-graph:
> > ./l3fwd-graph -c 0x100  -- -p 0x3 --config="(0, 0, 8)" -P
> >
> > [4]
> > # ./ip_pipeline --c 0xff0000 -- -s route.cli
> >
> > Route.cli: (Copy paste to the shell to avoid dos format issues)
> >
> > https://pastebin.com/raw/B4Ktx7TT
> >
> > Jerin Jacob (13):
> >    graph: define the public API for graph support
> >    graph: implement node registration
> >    graph: implement node operations
> >    graph: implement node debug routines
> >    graph: implement internal graph operation helpers
> >    graph: populate fastpath memory for graph reel
> >    graph: implement create and destroy APIs
> >    graph: implement graph operation APIs
> >    graph: implement Graphviz export
> >    graph: implement debug routines
> >    graph: implement stats support
> >    graph: implement fastpath API routines
> >    doc: add graph library programmer's guide guide
> >
> > Kiran Kumar K (2):
> >    graph: add unit test case
> >    node: add ipv4 rewrite node
> >
> > Nithin Dabilpuram (11):
> >    node: add log infra and null node
> >    node: add ethdev Rx node
> >    node: add ethdev Tx node
> >    node: add ethdev Rx and Tx node ctrl API
> >    node: ipv4 lookup for arm64
> >    node: add ipv4 rewrite and lookup ctrl API
> >    node: add packet drop node
> >    l3fwd-graph: add graph based l3fwd skeleton
> >    l3fwd-graph: add ethdev configuration changes
> >    l3fwd-graph: add graph config and main loop
> >    doc: add l3fwd graph application user guide
> >
> > Pavan Nikhilesh (3):
> >    graph: add performance testcase
> >    node: add generic ipv4 lookup node
> >    node: ipv4 lookup for x86
> >
> >   MAINTAINERS                                   |   14 +
> >   app/test/Makefile                             |    7 +
> >   app/test/meson.build                          |   12 +-
> >   app/test/test_graph.c                         |  819 ++++
> >   app/test/test_graph_perf.c                    | 1057 ++++++
> >   config/common_base                            |   12 +
> >   config/rte_config.h                           |    4 +
> >   doc/api/doxy-api-index.md                     |    5 +
> >   doc/api/doxy-api.conf.in                      |    2 +
> >   doc/guides/prog_guide/graph_lib.rst           |  397 ++
> >   .../prog_guide/img/anatomy_of_a_node.svg      | 1078 ++++++
> >   .../prog_guide/img/graph_mem_layout.svg       |  702 ++++
> >   doc/guides/prog_guide/img/link_the_nodes.svg  | 3330 +++++++++++++++++
> >   doc/guides/prog_guide/index.rst               |    1 +
> >   doc/guides/rel_notes/release_20_05.rst        |   32 +
> >   doc/guides/sample_app_ug/index.rst            |    1 +
> >   doc/guides/sample_app_ug/intro.rst            |    4 +
> >   doc/guides/sample_app_ug/l3_forward_graph.rst |  334 ++
> >   examples/Makefile                             |    3 +
> >   examples/l3fwd-graph/Makefile                 |   58 +
> >   examples/l3fwd-graph/main.c                   | 1126 ++++++
> >   examples/l3fwd-graph/meson.build              |   13 +
> >   examples/meson.build                          |    6 +-
> >   lib/Makefile                                  |    6 +
> >   lib/librte_graph/Makefile                     |   28 +
> >   lib/librte_graph/graph.c                      |  587 +++
> >   lib/librte_graph/graph_debug.c                |   84 +
> >   lib/librte_graph/graph_ops.c                  |  169 +
> >   lib/librte_graph/graph_populate.c             |  234 ++
> >   lib/librte_graph/graph_private.h              |  347 ++
> >   lib/librte_graph/graph_stats.c                |  406 ++
> >   lib/librte_graph/meson.build                  |   11 +
> >   lib/librte_graph/node.c                       |  421 +++
> >   lib/librte_graph/rte_graph.h                  |  668 ++++
> >   lib/librte_graph/rte_graph_version.map        |   47 +
> >   lib/librte_graph/rte_graph_worker.h           |  510 +++
> >   lib/librte_node/Makefile                      |   32 +
> >   lib/librte_node/ethdev_ctrl.c                 |  115 +
> >   lib/librte_node/ethdev_rx.c                   |  221 ++
> >   lib/librte_node/ethdev_rx_priv.h              |   81 +
> >   lib/librte_node/ethdev_tx.c                   |   86 +
> >   lib/librte_node/ethdev_tx_priv.h              |   62 +
> >   lib/librte_node/ip4_lookup.c                  |  215 ++
> >   lib/librte_node/ip4_lookup_neon.h             |  238 ++
> >   lib/librte_node/ip4_lookup_sse.h              |  244 ++
> >   lib/librte_node/ip4_rewrite.c                 |  326 ++
> >   lib/librte_node/ip4_rewrite_priv.h            |   77 +
> >   lib/librte_node/log.c                         |   14 +
> >   lib/librte_node/meson.build                   |   10 +
> >   lib/librte_node/node_private.h                |   79 +
> >   lib/librte_node/null.c                        |   23 +
> >   lib/librte_node/pkt_drop.c                    |   26 +
> >   lib/librte_node/rte_node_eth_api.h            |   64 +
> >   lib/librte_node/rte_node_ip4_api.h            |   78 +
> >   lib/librte_node/rte_node_version.map          |    9 +
> >   lib/meson.build                               |    5 +-
> >   meson.build                                   |    1 +
> >   mk/rte.app.mk                                 |    2 +
> >   58 files changed, 14538 insertions(+), 5 deletions(-)
> >   create mode 100644 app/test/test_graph.c
> >   create mode 100644 app/test/test_graph_perf.c
> >   create mode 100644 doc/guides/prog_guide/graph_lib.rst
> >   create mode 100644 doc/guides/prog_guide/img/anatomy_of_a_node.svg
> >   create mode 100644 doc/guides/prog_guide/img/graph_mem_layout.svg
> >   create mode 100644 doc/guides/prog_guide/img/link_the_nodes.svg
> >   create mode 100644 doc/guides/sample_app_ug/l3_forward_graph.rst
> >   create mode 100644 examples/l3fwd-graph/Makefile
> >   create mode 100644 examples/l3fwd-graph/main.c
> >   create mode 100644 examples/l3fwd-graph/meson.build
> >   create mode 100644 lib/librte_graph/Makefile
> >   create mode 100644 lib/librte_graph/graph.c
> >   create mode 100644 lib/librte_graph/graph_debug.c
> >   create mode 100644 lib/librte_graph/graph_ops.c
> >   create mode 100644 lib/librte_graph/graph_populate.c
> >   create mode 100644 lib/librte_graph/graph_private.h
> >   create mode 100644 lib/librte_graph/graph_stats.c
> >   create mode 100644 lib/librte_graph/meson.build
> >   create mode 100644 lib/librte_graph/node.c
> >   create mode 100644 lib/librte_graph/rte_graph.h
> >   create mode 100644 lib/librte_graph/rte_graph_version.map
> >   create mode 100644 lib/librte_graph/rte_graph_worker.h
> >   create mode 100644 lib/librte_node/Makefile
> >   create mode 100644 lib/librte_node/ethdev_ctrl.c
> >   create mode 100644 lib/librte_node/ethdev_rx.c
> >   create mode 100644 lib/librte_node/ethdev_rx_priv.h
> >   create mode 100644 lib/librte_node/ethdev_tx.c
> >   create mode 100644 lib/librte_node/ethdev_tx_priv.h
> >   create mode 100644 lib/librte_node/ip4_lookup.c
> >   create mode 100644 lib/librte_node/ip4_lookup_neon.h
> >   create mode 100644 lib/librte_node/ip4_lookup_sse.h
> >   create mode 100644 lib/librte_node/ip4_rewrite.c
> >   create mode 100644 lib/librte_node/ip4_rewrite_priv.h
> >   create mode 100644 lib/librte_node/log.c
> >   create mode 100644 lib/librte_node/meson.build
> >   create mode 100644 lib/librte_node/node_private.h
> >   create mode 100644 lib/librte_node/null.c
> >   create mode 100644 lib/librte_node/pkt_drop.c
> >   create mode 100644 lib/librte_node/rte_node_eth_api.h
> >   create mode 100644 lib/librte_node/rte_node_ip4_api.h
> >   create mode 100644 lib/librte_node/rte_node_version.map
> >

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v5 00/29] graph: introduce graph subsystem
  2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
                           ` (29 preceding siblings ...)
  2020-04-30  8:07         ` [dpdk-dev] [PATCH v5 00/29] graph: introduce graph subsystem Tom Barbette
@ 2020-05-05 21:44         ` Thomas Monjalon
  30 siblings, 0 replies; 219+ messages in thread
From: Thomas Monjalon @ 2020-05-05 21:44 UTC (permalink / raw)
  To: kirankumark, pbhagavatula, ndabilpuram, Jerin Jacob
  Cc: dev, david.marchand, mdr, mattias.ronnblom, xiao.w.wang, amo

11/04/2020 16:13, jerinj@marvell.com:
> From: Jerin Jacob <jerinj@marvell.com>
> 
> Using graph traversal for packet processing is a proven architecture
> that has been implemented in various open source libraries.
> 
> Graph architecture for packet processing enables abstracting the data
> processing functions as “nodes” and “links” them together to create a
> complex “graph” to create reusable/modular data processing functions. 
[...]
> Jerin Jacob (13):
>   graph: define the public API for graph support
>   graph: implement node registration
>   graph: implement node operations
>   graph: implement node debug routines
>   graph: implement internal graph operation helpers
>   graph: populate fastpath memory for graph reel
>   graph: implement create and destroy APIs
>   graph: implement graph operation APIs
>   graph: implement Graphviz export
>   graph: implement debug routines
>   graph: implement stats support
>   graph: implement fastpath API routines
>   doc: add graph library programmer's guide guide
> 
> Kiran Kumar K (2):
>   graph: add unit test case
>   node: add ipv4 rewrite node
> 
> Nithin Dabilpuram (11):
>   node: add log infra and null node
>   node: add ethdev Rx node
>   node: add ethdev Tx node
>   node: add ethdev Rx and Tx node ctrl API
>   node: ipv4 lookup for arm64
>   node: add ipv4 rewrite and lookup ctrl API
>   node: add packet drop node
>   l3fwd-graph: add graph based l3fwd skeleton
>   l3fwd-graph: add ethdev configuration changes
>   l3fwd-graph: add graph config and main loop
>   doc: add l3fwd graph application user guide
> 
> Pavan Nikhilesh (3):
>   graph: add performance testcase
>   node: add generic ipv4 lookup node
>   node: ipv4 lookup for x86

Applied with below small changes:
	- removed allow_experimental from libs
	- minor changes in MAINTAINERS
	- fixed SVG because of lines wrapped at 990 in email formatting

Thanks for the new experimental libraries.



^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v5 28/29] doc: add graph library programmer's guide guide
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 28/29] doc: add graph library programmer's guide guide jerinj
@ 2020-05-11  9:27           ` David Marchand
  2020-05-11  9:30             ` Jerin Jacob
  0 siblings, 1 reply; 219+ messages in thread
From: David Marchand @ 2020-05-11  9:27 UTC (permalink / raw)
  To: Jerin Jacob Kollanukkaran
  Cc: John McNamara, Marko Kovacevic, dev, Thomas Monjalon,
	Ray Kinsella, Mattias Rönnblom, Kiran Kumar Kokkilagadda,
	Pavan Nikhilesh, Nithin Dabilpuram, Xiao Wang, Andrzej Ostruszka

On Sat, Apr 11, 2020 at 4:16 PM <jerinj@marvell.com> wrote:

[snip]

> +``rte_graph_node_get_by_name()`` APIs can be used to to get the

A script of mine (that usually has a lot of false positives..) caught
this "to to get".
If some proofreading happens, worth fixing at the same time.

Thanks.

-- 
David Marchand


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v5 28/29] doc: add graph library programmer's guide guide
  2020-05-11  9:27           ` David Marchand
@ 2020-05-11  9:30             ` Jerin Jacob
  0 siblings, 0 replies; 219+ messages in thread
From: Jerin Jacob @ 2020-05-11  9:30 UTC (permalink / raw)
  To: David Marchand
  Cc: Jerin Jacob Kollanukkaran, John McNamara, Marko Kovacevic, dev,
	Thomas Monjalon, Ray Kinsella, Mattias Rönnblom,
	Kiran Kumar Kokkilagadda, Pavan Nikhilesh, Nithin Dabilpuram,
	Xiao Wang, Andrzej Ostruszka

On Mon, May 11, 2020 at 2:57 PM David Marchand
<david.marchand@redhat.com> wrote:
>
> On Sat, Apr 11, 2020 at 4:16 PM <jerinj@marvell.com> wrote:
>
> [snip]
>
> > +``rte_graph_node_get_by_name()`` APIs can be used to to get the
>
> A script of mine (that usually has a lot of false positives..) caught
> this "to to get".
> If some proofreading happens, worth fixing at the same time.

Ack and Thanks.

>
> Thanks.
>
> --
> David Marchand
>

^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [PATCH v5 20/29] node: ipv4 lookup for arm64
  2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 20/29] node: ipv4 lookup for arm64 jerinj
@ 2020-05-12  9:31           ` David Marchand
  2020-05-12  9:50             ` [dpdk-dev] [EXT] " Nithin Dabilpuram
  0 siblings, 1 reply; 219+ messages in thread
From: David Marchand @ 2020-05-12  9:31 UTC (permalink / raw)
  To: Jerin Jacob Kollanukkaran, Nithin Dabilpuram
  Cc: Pavan Nikhilesh, dev, Thomas Monjalon, Ray Kinsella,
	Mattias Rönnblom, Kiran Kumar Kokkilagadda, Xiao Wang,
	Andrzej Ostruszka

On Sat, Apr 11, 2020 at 4:16 PM <jerinj@marvell.com> wrote:
>
> From: Nithin Dabilpuram <ndabilpuram@marvell.com>
>
> Add arm64 specific IPv4 lookup process function
> for ip4_lookup node. This node performs LPM lookup
> on every packet received and forwards it to a next
> node that is identified by lookup result.
>
> Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> ---
>  lib/librte_node/ip4_lookup.c      |   6 +
>  lib/librte_node/ip4_lookup_neon.h | 238 ++++++++++++++++++++++++++++++
>  2 files changed, 244 insertions(+)
>  create mode 100644 lib/librte_node/ip4_lookup_neon.h

Checking OVS dpdk-latest branch, I caught a build issue on Ubuntu
16.04 for aarch64.
I reproduced it in travis by forcing the distribution to xenial in .travis.yml.

FAILED: gcc -Ilib/lib@@rte_node@sta -Ilib -I../lib -Ilib/librte_node
-I../lib/librte_node -I. -I../ -Iconfig -I../config
-Ilib/librte_eal/include -I../lib/librte_eal/include
-Ilib/librte_eal/linux/include -I../lib/librte_eal/linux/include
-Ilib/librte_eal/arm/include -I../lib/librte_eal/arm/include
-Ilib/librte_eal/common -I../lib/librte_eal/common -Ilib/librte_eal
-I../lib/librte_eal -Ilib/librte_kvargs -I../lib/librte_kvargs
-Ilib/librte_telemetry/../librte_metrics
-I../lib/librte_telemetry/../librte_metrics -Ilib/librte_telemetry
-I../lib/librte_telemetry -Ilib/librte_graph -I../lib/librte_graph
-Ilib/librte_mbuf -I../lib/librte_mbuf -Ilib/librte_mempool
-I../lib/librte_mempool -Ilib/librte_ring -I../lib/librte_ring
-Ilib/librte_lpm -I../lib/librte_lpm -Ilib/librte_hash
-I../lib/librte_hash -Ilib/librte_ethdev -I../lib/librte_ethdev
-Ilib/librte_net -I../lib/librte_net -Ilib/librte_meter
-I../lib/librte_meter -Ilib/librte_cryptodev -I../lib/librte_cryptodev
-fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -Wall
-Winvalid-pch -Werror -O2 -g -include rte_config.h -Wextra -Wcast-qual
-Wdeprecated -Wformat-nonliteral -Wformat-security
-Wmissing-declarations -Wmissing-prototypes -Wnested-externs
-Wold-style-definition -Wpointer-arith -Wsign-compare
-Wstrict-prototypes -Wundef -Wwrite-strings
-Wno-missing-field-initializers -D_GNU_SOURCE -fPIC
-DALLOW_EXPERIMENTAL_API -DALLOW_INTERNAL_API -fno-strict-aliasing
-MD -MQ 'lib/lib@@rte_node@sta/librte_node_ip4_lookup.c.o' -MF
'lib/lib@@rte_node@sta/librte_node_ip4_lookup.c.o.d' -o
'lib/lib@@rte_node@sta/librte_node_ip4_lookup.c.o' -c
../lib/librte_node/ip4_lookup.c
In file included from ../lib/librte_node/ip4_lookup.c:34:0:
../lib/librte_node/ip4_lookup_neon.h: In function ‘ip4_lookup_node_process’:
../lib/librte_node/ip4_lookup_neon.h:25:12: error: ‘dip’ may be used
uninitialized in this function [-Werror=maybe-uninitialized]
  int32x4_t dip;
            ^
cc1: all warnings being treated as errors
ninja: build stopped: subcommand failed.


The odd thing is that a more recent gcc does not complain.
So there must be a catch, can you have a look?
Thanks.


-- 
David Marchand


^ permalink raw reply	[flat|nested] 219+ messages in thread

* Re: [dpdk-dev] [EXT] Re: [PATCH v5 20/29] node: ipv4 lookup for arm64
  2020-05-12  9:31           ` David Marchand
@ 2020-05-12  9:50             ` Nithin Dabilpuram
  0 siblings, 0 replies; 219+ messages in thread
From: Nithin Dabilpuram @ 2020-05-12  9:50 UTC (permalink / raw)
  To: David Marchand
  Cc: Jerin Jacob Kollanukkaran, Pavan Nikhilesh, dev, Thomas Monjalon,
	Ray Kinsella, Mattias Rönnblom, Kiran Kumar Kokkilagadda,
	Xiao Wang, Andrzej Ostruszka

On Tue, May 12, 2020 at 11:31:26AM +0200, David Marchand wrote:
> External Email
> 
> ----------------------------------------------------------------------
> On Sat, Apr 11, 2020 at 4:16 PM <jerinj@marvell.com> wrote:
> >
> > From: Nithin Dabilpuram <ndabilpuram@marvell.com>
> >
> > Add arm64 specific IPv4 lookup process function
> > for ip4_lookup node. This node performs LPM lookup
> > on every packet received and forwards it to a next
> > node that is identified by lookup result.
> >
> > Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
> > Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
> > Signed-off-by: Pavan Nikhilesh <pbhagavatula@marvell.com>
> > ---
> >  lib/librte_node/ip4_lookup.c      |   6 +
> >  lib/librte_node/ip4_lookup_neon.h | 238 ++++++++++++++++++++++++++++++
> >  2 files changed, 244 insertions(+)
> >  create mode 100644 lib/librte_node/ip4_lookup_neon.h
> 
> Checking OVS dpdk-latest branch, I caught a build issue on Ubuntu
> 16.04 for aarch64.
> I reproduced it in travis by forcing the distribution to xenial in .travis.yml.
> 
> FAILED: gcc -Ilib/lib@@rte_node@sta -Ilib -I../lib -Ilib/librte_node
> -I../lib/librte_node -I. -I../ -Iconfig -I../config
> -Ilib/librte_eal/include -I../lib/librte_eal/include
> -Ilib/librte_eal/linux/include -I../lib/librte_eal/linux/include
> -Ilib/librte_eal/arm/include -I../lib/librte_eal/arm/include
> -Ilib/librte_eal/common -I../lib/librte_eal/common -Ilib/librte_eal
> -I../lib/librte_eal -Ilib/librte_kvargs -I../lib/librte_kvargs
> -Ilib/librte_telemetry/../librte_metrics
> -I../lib/librte_telemetry/../librte_metrics -Ilib/librte_telemetry
> -I../lib/librte_telemetry -Ilib/librte_graph -I../lib/librte_graph
> -Ilib/librte_mbuf -I../lib/librte_mbuf -Ilib/librte_mempool
> -I../lib/librte_mempool -Ilib/librte_ring -I../lib/librte_ring
> -Ilib/librte_lpm -I../lib/librte_lpm -Ilib/librte_hash
> -I../lib/librte_hash -Ilib/librte_ethdev -I../lib/librte_ethdev
> -Ilib/librte_net -I../lib/librte_net -Ilib/librte_meter
> -I../lib/librte_meter -Ilib/librte_cryptodev -I../lib/librte_cryptodev
> -fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -Wall
> -Winvalid-pch -Werror -O2 -g -include rte_config.h -Wextra -Wcast-qual
> -Wdeprecated -Wformat-nonliteral -Wformat-security
> -Wmissing-declarations -Wmissing-prototypes -Wnested-externs
> -Wold-style-definition -Wpointer-arith -Wsign-compare
> -Wstrict-prototypes -Wundef -Wwrite-strings
> -Wno-missing-field-initializers -D_GNU_SOURCE -fPIC
> -DALLOW_EXPERIMENTAL_API -DALLOW_INTERNAL_API -fno-strict-aliasing
> -MD -MQ 'lib/lib@@rte_node@sta/librte_node_ip4_lookup.c.o' -MF
> 'lib/lib@@rte_node@sta/librte_node_ip4_lookup.c.o.d' -o
> 'lib/lib@@rte_node@sta/librte_node_ip4_lookup.c.o' -c
> ../lib/librte_node/ip4_lookup.c
> In file included from ../lib/librte_node/ip4_lookup.c:34:0:
> ../lib/librte_node/ip4_lookup_neon.h: In function ‘ip4_lookup_node_process’:
> ../lib/librte_node/ip4_lookup_neon.h:25:12: error: ‘dip’ may be used
> uninitialized in this function [-Werror=maybe-uninitialized]
>   int32x4_t dip;
>             ^
> cc1: all warnings being treated as errors
> ninja: build stopped: subcommand failed.
> 
> 
> The odd thing is that a more recent gcc does not complain.
> So there must be a catch, can you have a look?
> Thanks.
> 

Sure. Thanks. Will test and get back.
> 
> -- 
> David Marchand
> 

^ permalink raw reply	[flat|nested] 219+ messages in thread

end of thread, other threads:[~2020-05-12  9:50 UTC | newest]

Thread overview: 219+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-18 21:35 [dpdk-dev] [PATCH v1 00/26] graph: introduce graph subsystem jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 01/26] graph: define the public API for graph support jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 02/26] graph: implement node registration jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 03/26] graph: implement node operations jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 04/26] graph: implement node debug routines jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 05/26] graph: implement internal graph operation helpers jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 06/26] graph: populate fastpath memory for graph reel jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 07/26] graph: implement create and destroy APIs jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 08/26] graph: implement graph operation APIs jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 09/26] graph: implement Graphviz export jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 10/26] graph: implement debug routines jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 11/26] graph: implement stats support jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 12/26] graph: implement fastpath API routines jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 13/26] graph: add unit test case jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 14/26] graph: add performance testcase jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 15/26] node: add log infra and null node jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 16/26] node: add ethdev Rx node jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 17/26] node: add ethdev Tx node jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 18/26] node: add ethdev Rx and Tx node ctrl API jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 19/26] node: ipv4 lookup for arm64 jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 20/26] node: ipv4 lookup for x86 jerinj
2020-03-19 12:25   ` Ray Kinsella
2020-03-19 14:22     ` [dpdk-dev] [EXT] " Pavan Nikhilesh Bhagavatula
2020-03-19 15:50       ` Ray Kinsella
2020-03-19 16:13         ` Pavan Nikhilesh Bhagavatula
2020-03-20  9:14           ` Ray Kinsella
2020-03-24  9:40             ` Pavan Nikhilesh Bhagavatula
2020-03-24 14:38               ` Ray Kinsella
2020-03-26  9:56                 ` Pavan Nikhilesh Bhagavatula
2020-03-26 16:54                   ` Ray Kinsella
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 21/26] node: add ipv4 rewrite node jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 22/26] node: add ipv4 rewrite and lookup ctrl API jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 23/26] node: add pkt drop node jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 24/26] l3fwd-graph: add graph based l3fwd skeleton jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 25/26] l3fwd-graph: add ethdev configuration changes jerinj
2020-03-18 21:35 ` [dpdk-dev] [PATCH v1 26/26] l3fwd-graph: add graph config and main loop jerinj
2020-03-26 16:56 ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 01/28] graph: define the public API for graph support jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 02/28] graph: implement node registration jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 03/28] graph: implement node operations jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 04/28] graph: implement node debug routines jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 05/28] graph: implement internal graph operation helpers jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 06/28] graph: populate fastpath memory for graph reel jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 07/28] graph: implement create and destroy APIs jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 08/28] graph: implement graph operation APIs jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 09/28] graph: implement Graphviz export jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 10/28] graph: implement debug routines jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 11/28] graph: implement stats support jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 12/28] graph: implement fastpath API routines jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 13/28] graph: add unit test case jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 14/28] graph: add performance testcase jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 15/28] node: add log infra and null node jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 16/28] node: add ethdev Rx node jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 17/28] node: add ethdev Tx node jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 18/28] node: add ethdev Rx and Tx node ctrl API jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 19/28] node: ipv4 lookup for arm64 jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 20/28] node: ipv4 lookup for x86 jerinj
2020-03-27  8:40     ` Jerin Jacob
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 21/28] node: add ipv4 rewrite node jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 22/28] node: add ipv4 rewrite and lookup ctrl API jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 23/28] node: add pkt drop node jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 24/28] l3fwd-graph: add graph based l3fwd skeleton jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 25/28] l3fwd-graph: add ethdev configuration changes jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 26/28] l3fwd-graph: add graph config and main loop jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 27/28] doc: add graph library programmer's guide guide jerinj
2020-03-26 16:56   ` [dpdk-dev] [PATCH v2 28/28] doc: add l3fwd graph application user guide jerinj
2020-03-27  6:49   ` [dpdk-dev] [PATCH v2 00/28] graph: introduce graph subsystem Jerin Jacob
2020-03-27 10:42     ` Thomas Monjalon
2020-03-31 19:29   ` [dpdk-dev] [PATCH v3 00/29] " jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 01/29] graph: define the public API for graph support jerinj
2020-04-03  9:26       ` Wang, Xiao W
2020-04-04 12:15         ` Jerin Jacob
2020-04-06 12:36       ` Andrzej Ostruszka
2020-04-06 14:59         ` Jerin Jacob
2020-04-06 16:09           ` Andrzej Ostruszka
2020-04-07 10:27             ` Jerin Jacob
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 02/29] graph: implement node registration jerinj
2020-04-03 10:44       ` Wang, Xiao W
2020-04-04 12:29         ` Jerin Jacob
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 03/29] graph: implement node operations jerinj
2020-04-03 10:54       ` Wang, Xiao W
2020-04-04 13:07         ` Jerin Jacob
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 04/29] graph: implement node debug routines jerinj
2020-04-04  7:57       ` Wang, Xiao W
2020-04-04 13:12         ` Jerin Jacob
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 05/29] graph: implement internal graph operation helpers jerinj
2020-04-06 13:47       ` Wang, Xiao W
2020-04-06 14:08         ` Jerin Jacob
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 06/29] graph: populate fastpath memory for graph reel jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 07/29] graph: implement create and destroy APIs jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 08/29] graph: implement graph operation APIs jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 09/29] graph: implement Graphviz export jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 10/29] graph: implement debug routines jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 11/29] graph: implement stats support jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 12/29] graph: implement fastpath API routines jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 13/29] graph: add unit test case jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 14/29] graph: add performance testcase jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 15/29] node: add log infra and null node jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 16/29] node: add ethdev Rx node jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 17/29] node: add ethdev Tx node jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 19/29] node: add generic ipv4 lookup node jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 20/29] node: ipv4 lookup for arm64 jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 21/29] node: ipv4 lookup for x86 jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 22/29] node: add ipv4 rewrite node jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 24/29] node: add packet drop node jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 26/29] l3fwd-graph: add ethdev configuration changes jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 27/29] l3fwd-graph: add graph config and main loop jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 28/29] doc: add graph library programmer's guide guide jerinj
2020-03-31 19:29     ` [dpdk-dev] [PATCH v3 29/29] doc: add l3fwd graph application user guide jerinj
2020-04-05  8:55     ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 01/29] graph: define the public API for graph support jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 02/29] graph: implement node registration jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 03/29] graph: implement node operations jerinj
2020-04-06 17:57         ` Andrzej Ostruszka
2020-04-07  2:43           ` [dpdk-dev] [EXT] " Kiran Kumar Kokkilagadda
2020-04-07  8:47             ` Andrzej Ostruszka
2020-04-07 10:20             ` Jerin Jacob
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 04/29] graph: implement node debug routines jerinj
2020-04-06 18:17         ` Andrzej Ostruszka
2020-04-07 10:22           ` Jerin Jacob
2020-04-07 11:50             ` Andrzej Ostruszka
2020-04-07 12:09               ` Jerin Jacob
2020-04-07 12:50                 ` Andrzej Ostruszka
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 05/29] graph: implement internal graph operation helpers jerinj
2020-04-07 12:16         ` Andrzej Ostruszka
2020-04-07 12:27           ` Jerin Jacob
2020-04-07 12:54             ` Andrzej Ostruszka
2020-04-07 13:31               ` Jerin Jacob
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 06/29] graph: populate fastpath memory for graph reel jerinj
2020-04-08 17:30         ` Andrzej Ostruszka
2020-04-09  2:44           ` Kiran Kumar Kokkilagadda
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 07/29] graph: implement create and destroy APIs jerinj
2020-04-08 16:57         ` Andrzej Ostruszka
2020-04-08 17:23           ` Jerin Jacob
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 08/29] graph: implement graph operation APIs jerinj
2020-04-08 17:49         ` Andrzej Ostruszka
2020-04-08 19:18           ` Jerin Jacob
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 09/29] graph: implement Graphviz export jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 10/29] graph: implement debug routines jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 11/29] graph: implement stats support jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 12/29] graph: implement fastpath API routines jerinj
2020-04-09 23:07         ` Andrzej Ostruszka
2020-04-10  9:18           ` Jerin Jacob
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 13/29] graph: add unit test case jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 14/29] graph: add performance testcase jerinj
2020-04-05  8:55       ` [dpdk-dev] [PATCH v4 15/29] node: add log infra and null node jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 16/29] node: add ethdev Rx node jerinj
2020-04-09 23:05         ` Andrzej Ostruszka
2020-04-10  7:00           ` Nithin Dabilpuram
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 17/29] node: add ethdev Tx node jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
2020-04-09 23:07         ` Andrzej Ostruszka
2020-04-10  5:09           ` Nithin Dabilpuram
2020-04-10  8:22             ` Nithin Dabilpuram
2020-04-10 12:52             ` Andrzej Ostruszka
2020-04-10 14:54               ` [dpdk-dev] [EXT] " Nithin Dabilpuram
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 19/29] node: add generic ipv4 lookup node jerinj
2020-04-09 23:07         ` Andrzej Ostruszka
2020-04-10 10:20           ` Nithin Dabilpuram
2020-04-10 14:41             ` Nithin Dabilpuram
2020-04-10 15:17               ` Andrzej Ostruszka
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 20/29] node: ipv4 lookup for arm64 jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 21/29] node: ipv4 lookup for x86 jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 22/29] node: add ipv4 rewrite node jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
2020-04-09 23:04         ` Andrzej Ostruszka
2020-04-10  7:24           ` Nithin Dabilpuram
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 24/29] node: add packet drop node jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
2020-04-09 23:04         ` Andrzej Ostruszka
2020-04-10  8:23           ` Nithin Dabilpuram
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 26/29] l3fwd-graph: add ethdev configuration changes jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 27/29] l3fwd-graph: add graph config and main loop jerinj
2020-04-09 23:04         ` Andrzej Ostruszka
2020-04-10  9:29           ` Nithin Dabilpuram
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 28/29] doc: add graph library programmer's guide guide jerinj
2020-04-05  8:56       ` [dpdk-dev] [PATCH v4 29/29] doc: add l3fwd graph application user guide jerinj
2020-04-09 23:13       ` [dpdk-dev] [PATCH v4 00/29] graph: introduce graph subsystem Andrzej Ostruszka
2020-04-10  9:07         ` Jerin Jacob
2020-04-11 14:13       ` [dpdk-dev] [PATCH v5 " jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 01/29] graph: define the public API for graph support jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 02/29] graph: implement node registration jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 03/29] graph: implement node operations jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 04/29] graph: implement node debug routines jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 05/29] graph: implement internal graph operation helpers jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 06/29] graph: populate fastpath memory for graph reel jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 07/29] graph: implement create and destroy APIs jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 08/29] graph: implement graph operation APIs jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 09/29] graph: implement Graphviz export jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 10/29] graph: implement debug routines jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 11/29] graph: implement stats support jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 12/29] graph: implement fastpath API routines jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 13/29] graph: add unit test case jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 14/29] graph: add performance testcase jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 15/29] node: add log infra and null node jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 16/29] node: add ethdev Rx node jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 17/29] node: add ethdev Tx node jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 18/29] node: add ethdev Rx and Tx node ctrl API jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 19/29] node: add generic ipv4 lookup node jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 20/29] node: ipv4 lookup for arm64 jerinj
2020-05-12  9:31           ` David Marchand
2020-05-12  9:50             ` [dpdk-dev] [EXT] " Nithin Dabilpuram
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 21/29] node: ipv4 lookup for x86 jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 22/29] node: add ipv4 rewrite node jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 23/29] node: add ipv4 rewrite and lookup ctrl API jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 24/29] node: add packet drop node jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 25/29] l3fwd-graph: add graph based l3fwd skeleton jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 26/29] l3fwd-graph: add ethdev configuration changes jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 27/29] l3fwd-graph: add graph config and main loop jerinj
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 28/29] doc: add graph library programmer's guide guide jerinj
2020-05-11  9:27           ` David Marchand
2020-05-11  9:30             ` Jerin Jacob
2020-04-11 14:14         ` [dpdk-dev] [PATCH v5 29/29] doc: add l3fwd graph application user guide jerinj
2020-04-30  8:07         ` [dpdk-dev] [PATCH v5 00/29] graph: introduce graph subsystem Tom Barbette
2020-04-30  8:42           ` Jerin Jacob
2020-05-05 21:44         ` Thomas Monjalon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).