DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v9 01/26] net/i40e: support link status notification
From: Vincent JARDIN @ 2017-01-15 23:35 UTC (permalink / raw)
  To: Wenzhuo Lu; +Cc: dev
In-Reply-To: <1484290401-1404-2-git-send-email-wenzhuo.lu@intel.com>

Le 13/01/2017 à 07:52, Wenzhuo Lu a écrit :
> + * @b EXPERIMENTAL: this API may change without prior notice

a minor comment here:
   + including be removed.

^ permalink raw reply

* Re: [PATCH v5 00/29] Support VFD and DPDK PF + kernel VF on i40e
From: Vincent JARDIN @ 2017-01-15 23:32 UTC (permalink / raw)
  To: JOSHI, KAUSTUBH (KAUSTUBH), John Fastabend
  Cc: Chen, Jing D, DANIELS, EDWARD S (EDWARD), dev@dpdk.org,
	ZELEZNIAK, ALEX, Fastabend, John R, Ferruh Yigit
In-Reply-To: <6FE891CC-F927-4719-B7F8-BCF8EB4DEF9F@research.att.com>

Thanks John for the kernel support. I'd like to add one thought on this 
need from Joshi:

> Also, the kernel drivers have no concept of passing VF messages to upstream "decision making” (or policy enforcement) software like VFd.

It shall be possible to define some Netlink or policy management 
messages over the PF, so a userland process can police and provide the 
"decision making" in a standard way that would be generic.

Joshi,

John is confirming that the kernel path is possible. Please, if you see 
other requirements, keep listing them.

Le 14/01/2017 à 00:49, JOSHI, KAUSTUBH  (KAUSTUBH) a écrit :
> However, in theory, this *can* be done through an extra VF as well, so I didn't bring up that particular point as being a deciding factor in this debate.

It remains an important item: it shows that thru the kernel, all 
technical requirements are manageable, including the steering of traffic.

I'll continue the comments on the patch on the v9's thread.
   http://dpdk.org/ml/archives/dev/2017-January/054953.html

Best regards,
   Vincent

^ permalink raw reply

* Re: [PATCH v3 11/29] eal: generic implementation for I/O device read/write access
From: Thomas Monjalon @ 2017-01-15 21:29 UTC (permalink / raw)
  To: Jerin Jacob
  Cc: dev, konstantin.ananyev, bruce.richardson, jianbo.liu, viktorin,
	santosh.shukla
In-Reply-To: <1484212646-10338-12-git-send-email-jerin.jacob@caviumnetworks.com>

2017-01-12 14:47, Jerin Jacob:
> +#define rte_read8_relaxed(addr) \
> +	({ uint8_t __v = *(const volatile uint8_t *)addr; __v; })

Why do you prefer a macro over an inline function?
It won't provide the same "debuggability".

^ permalink raw reply

* Re: [PATCH v8 1/5] ethdev: add firmware version get
From: Thomas Monjalon @ 2017-01-15 20:56 UTC (permalink / raw)
  To: Qiming Yang; +Cc: dev, ferruh.yigit, remy.horton
In-Reply-To: <1484202716-41669-2-git-send-email-qiming.yang@intel.com>

2017-01-12 14:31, Qiming Yang:
> + * @param fw_size
> + *   The size of the array pointed by fw_version, which should be
> + *   large enough to store firmware version of the device.
> + * @return
> + *   - (0) if successful.
> + *   - (-ENOTSUP) if operation is not supported.
> + *   - (-ENODEV) if *port_id* invalid.
> + *   - (-EINVAL) if *fw_size* is not enough to store firmware version.
> + */
> +int rte_eth_dev_fw_version_get(uint8_t port_id,
> +                              char *fw_version, size_t fw_size);
> +

How to know the required size?
You should return the size of the non truncated string as snprintf does.
Note: Andrew Rybchenko already commented on it and you didn't reply.

^ permalink raw reply

* [PATCH] net/qede: correct function declaration syntax
From: Emmanuel Roullit @ 2017-01-15 19:51 UTC (permalink / raw)
  To: rasesh.mody; +Cc: dev

clang reports this error message:
error: this function declaration is not a prototype
[-Werror,-Wstrict-prototypes]

Signed-off-by: Emmanuel Roullit <emmanuel.roullit@gmail.com>
---
 drivers/net/qede/qede_eth_if.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/qede/qede_eth_if.h b/drivers/net/qede/qede_eth_if.h
index 33655c3..74e17f8 100644
--- a/drivers/net/qede/qede_eth_if.h
+++ b/drivers/net/qede/qede_eth_if.h
@@ -171,7 +171,7 @@ struct qed_eth_ops {
 
 extern const struct qed_common_ops qed_common_ops_pass;
 
-const struct qed_eth_ops *qed_get_eth_ops();
+const struct qed_eth_ops *qed_get_eth_ops(void);
 
 int qed_configure_filter_rx_mode(struct ecore_dev *edev,
 				 enum qed_filter_rx_mode_type type);
-- 
1.9.1

^ permalink raw reply related

* [PATCH] net/cxgbe: add parenthesis on bitwise operation
From: Emmanuel Roullit @ 2017-01-15 19:50 UTC (permalink / raw)
  To: rahul.lakkireddy; +Cc: dev

clang reports the following error:
error: logical not is only applied to the left hand side of this bitwise
operator. [-Werror,-Wlogical-not-parentheses]

Signed-off-by: Emmanuel Roullit <emmanuel.roullit@gmail.com>
---
 drivers/net/cxgbe/cxgbe_main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c
index 7ccf434..541fc40 100644
--- a/drivers/net/cxgbe/cxgbe_main.c
+++ b/drivers/net/cxgbe/cxgbe_main.c
@@ -959,7 +959,7 @@ int setup_rss(struct port_info *pi)
 	dev_debug(adapter, "%s:  pi->rss_size = %u; pi->n_rx_qsets = %u\n",
 		  __func__, pi->rss_size, pi->n_rx_qsets);
 
-	if (!pi->flags & PORT_RSS_DONE) {
+	if (!(pi->flags & PORT_RSS_DONE)) {
 		if (adapter->flags & FULL_INIT_DONE) {
 			/* Fill default values with equal distribution */
 			for (j = 0; j < pi->rss_size; j++)
-- 
1.9.1

^ permalink raw reply related

* [PATCH] mk: suppress reference of packed members
From: Emmanuel Roullit @ 2017-01-15 19:47 UTC (permalink / raw)
  To: thomas.monjalon; +Cc: dev

error: taking address of packed member 'mlock' of class or structure
'rte_mem_config' may result in an unaligned pointer value
[-Werror,-Waddress-of-packed-member]

Signed-off-by: Emmanuel Roullit <emmanuel.roullit@gmail.com>
---
 mk/toolchain/clang/rte.vars.mk | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mk/toolchain/clang/rte.vars.mk b/mk/toolchain/clang/rte.vars.mk
index 7749b99..04746be 100644
--- a/mk/toolchain/clang/rte.vars.mk
+++ b/mk/toolchain/clang/rte.vars.mk
@@ -67,7 +67,7 @@ WERROR_FLAGS := -W -Wall -Wstrict-prototypes -Wmissing-prototypes
 WERROR_FLAGS += -Wmissing-declarations -Wold-style-definition -Wpointer-arith
 WERROR_FLAGS += -Wnested-externs -Wcast-qual
 WERROR_FLAGS += -Wformat-nonliteral -Wformat-security
-WERROR_FLAGS += -Wundef -Wwrite-strings
+WERROR_FLAGS += -Wundef -Wwrite-strings -Wno-address-of-packed-member
 
 ifeq ($(RTE_DEVEL_BUILD),y)
 WERROR_FLAGS += -Werror
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH v5] mbuf: add a function to linearize a packet
From: Thomas Monjalon @ 2017-01-15 18:32 UTC (permalink / raw)
  To: Tomasz Kulasek; +Cc: dev, Olivier Matz, Pablo de Lara
In-Reply-To: <20170113163234.1f38b9ee@platinum>

2017-01-13 16:32, Olivier Matz:
> On Thu, 12 Jan 2017 10:40:44 +0100, Tomasz Kulasek
> <tomaszx.kulasek@intel.com> wrote:
> > This patch adds function rte_pktmbuf_linearize to let crypto PMD
> > coalesce chained mbuf before crypto operation and extend their
> > capabilities to support segmented mbufs when device cannot handle
> > them natively.
> > 
> > Included unit tests for rte_pktmbuf_linearize functionality:
> > 
> >  1) Creates banch of segmented mbufs with different size and number of
> >     segments.
> >  2) Fills noncontigouos mbuf with sequential values.
> >  3) Uses rte_pktmbuf_linearize to coalesce segmented buffer into one
> >     contiguous.
> >  4) Verifies data in linearized buffer.
> > 
> > Dependencies:
> > 
> > This patch is rebased to the dpdk-next-crypto and should be applied
> > before "Chained Mbufs support in SW PMDs" patchset.
> > 
> > changes in v5:
> >  - name of patch changed,
> >  - improved coding style,
> > 
> > changes in v4:
> >  - separated from "Chained Mbufs support in SW PMDs" patch set for
> >    better reviewing,
> >  - merged "rte_pktmbuf_linearize" implementation with unit tests,
> > 
> > changes in v3:
> >  - rebased to dpdk-next-crypto
> > 
> > changes in v2:
> >  - rte_pktmbuf_coalesce replaced with rte_pktmbuf_linearize
> > 
> > Cc: Pablo de Lara <pablo.de.lara.guarch@intel.com>
> > Cc: Olivier Matz <olivier.matz@6wind.com>
> > Signed-off-by: Tomasz Kulasek <tomaszx.kulasek@intel.com>
> 
> Acked-by: Olivier Matz <olivier.matz@6wind.com>

Applied, thanks

^ permalink raw reply

* Re: [PATCH v7 0/6] Add MACsec offload support for ixgbe
From: Thomas Monjalon @ 2017-01-15 18:19 UTC (permalink / raw)
  To: Tiwei Bie
  Cc: dev, adrien.mazarguil, wenzhuo.lu, john.mcnamara, olivier.matz,
	konstantin.ananyev, helin.zhang, wei.dai, xiao.w.wang
In-Reply-To: <1484306501-164565-1-git-send-email-tiwei.bie@intel.com>

2017-01-13 19:21, Tiwei Bie:
> This patch set adds the MACsec offload support for ixgbe.
> The testpmd is also updated to support MACsec cmds.

Applied, thanks

Minor updates when applying:
- no need of release notes section for mbuf flag (covered by ixgbe entry)
- squashed release notes patch with ixgbe patch

^ permalink raw reply

* Re: 17.05 Roadmap
From: Hanoch Haim (hhaim) @ 2017-01-15 14:42 UTC (permalink / raw)
  To: O'Driscoll, Tim, dev@dpdk.org; +Cc: Hanoch Haim (hhaim)
In-Reply-To: <26FA93C7ED1EAA44AB77D62FBE1D27BA722A7A17@IRSMSX108.ger.corp.intel.com>

Hi Tim, 
It's great that you are going to expose the i40E Qos capability, would it be possible to expose the low-latency configuration too?


Thanks,
Hanoh
-----Original Message-----
From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of O'Driscoll, Tim
Sent: Friday, January 13, 2017 12:24 PM
To: dev@dpdk.org
Subject: [dpdk-dev] 17.05 Roadmap

Below are the features that we're planning to submit for the 17.05 release. We'll submit a patch to update the roadmap page with this info.

It would be good if others are also willing to share their plans so that we can build up a complete picture of what's planned for 17.05 and make sure there's no duplication.


Cryptodev DES SW PMD: A PMD will be added which implements an optimized software version of the DES algorithm.

Cryptodev DOCSIS BPI+: The cryptodev API will be enhanced to enable processing of packets according to the Baseline Privacy Interface Plus (BPI+) specification described in the Data-over-Cable Service Interface Specification (DOCSIS) Security Specification (http://www.cablelabs.com/wp-content/uploads/specdocs/CM-SP-SECv3.1-I06-160602.pdf). See the RFC (http://dpdk.org/ml/archives/dev/2016-December/052433.html) for further details. Support will be added to the QAT PMD for AES and DES, to the new software PMD for DES (see previous item), and to the existing AESNI_MB PMD.

Cryptodev Scheduler: This allows packets to be encrypted/decrypted in either SW or HW depending on packet size and HW utilization. Reordering will be supported so that packet order can be preserved.

Ethernet 32-bit CRC Generation: An optimized x86 library for CRC-32 will be added. A CRC may need to be generated for a downstream frame by the DOCSIS MAC layer because the CRC may be removed by the NIC or the frame may be modified by the MAC for PHS (packet header suppression). 

API to Configure Programmable Devices: More devices are now supporting programmable components, for example the Pipeline Personalization Profiles in I40E. An API will be added to allow any programmable device to be configured.

I40E Hardware QoS: Hardware QoS will be supported on the I40E. This will include Tx bandwidth control (min and max), and Rx Traffic Class assignment.

Configurable Tunnel Filters for I40E: DPDK support will be added for a new I40E admin queue which allows configuration of filter types for cloud filters.

Enable MPLS on I40E: MPLSoUDP and MPLSoGRE will be supported for I40E, including the new protocols and filtering (RSS and Flow Director), checksum offload and packet type recognition.

Software Eventdev Implementation: The libeventdev API is being added in 17.02 (http://dpdk.org/ml/archives/dev/2016-December/052877.html). A software implementation of this API will be added.

New Vhost Device Type: The vhost-user framework will be expanded so that it can support additional device types. Support for SCSI will be added initially, but block devices and other device types may be added in future.

Abstraction Layer for QoS: An abstraction layer for Quality of Service (QoS) hierarchical schedulers will be implemented. The goal of the abstraction layer is to provide a simple generic API that is agnostic of the underlying HW, SW or mixed HW-SW implementation. See the RFC (http://dpdk.org/ml/archives/dev/2016-November/050956.html) for details.

IPFIX Support: The Internet Protocol Flow Information Export (IPFIX) protocol provides network administrators with access to IP Flow information. An observation point (one for all the interfaces), metering process and an exporter process will be implemented. The observation point and metering process will be as defined in RFC 5474 (https://tools.ietf.org/html/rfc5474).

Interrupt Mode for Virtio-User: Interrupt mode support will be added for virtio-user, which is a virtual device for high performance container networking.

Automated VF Processing of PF Reset for I40E: In 16.07, changes were made for both IXGBE and I40E to notify a VF when a PF reset occurs. This will be further enhanced for I40E so that the driver handles as much of the processing as possible, including things like resetting VF ports.

Tx Buffering in Vhost PMD: Overall throughput drops as the number of guests increases on a single host.  This is because a Tx burst is spread across multiple guests, so the burst size per guest decreases as the number of guests increases. Tx buffering will be added which will increase the Tx burst size and increase performance.

Consistent PMD Batching Behaviour: PMD Tx batching behavior is not consistent between PMDs. Some PMDs have no limit for nb_pkts (besides number of available descriptors), some (like vhost) have a limit on the max number of Tx packets (32 for vhost), and some define a max burst size and transmit packets in multiple bursts up to that size. The application needs to manage these differences. To make things easier for application developers, we're considering putting the logic for managing this directly into rte_eth_tx_burst.

^ permalink raw reply

* [PATCH v4 5/5] doc: add flow distributor guide
From: Pablo de Lara @ 2017-01-15 12:04 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara, Sameh Gobriel
In-Reply-To: <1484481875-126335-1-git-send-email-pablo.de.lara.guarch@intel.com>

Signed-off-by: Sameh Gobriel <sameh.gobriel@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Acked-by: Christian Maciocco <christian.maciocco@intel.com>
---
 MAINTAINERS                                       |    1 +
 doc/guides/sample_app_ug/flow_distributor.rst     |  494 ++++++++
 doc/guides/sample_app_ug/img/flow_distributor.svg | 1254 +++++++++++++++++++++
 doc/guides/sample_app_ug/index.rst                |    1 +
 4 files changed, 1750 insertions(+)
 create mode 100644 doc/guides/sample_app_ug/flow_distributor.rst
 create mode 100644 doc/guides/sample_app_ug/img/flow_distributor.svg

diff --git a/MAINTAINERS b/MAINTAINERS
index 66e9466..0d3b247 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -535,6 +535,7 @@ F: lib/librte_efd/
 F: doc/guides/prog_guide/efd_lib.rst
 F: app/test/test_efd*
 F: examples/flow_distributor/
+F: doc/guides/sample_app_ug/flow_distributor.rst
 
 Hashes
 M: Bruce Richardson <bruce.richardson@intel.com>
diff --git a/doc/guides/sample_app_ug/flow_distributor.rst b/doc/guides/sample_app_ug/flow_distributor.rst
new file mode 100644
index 0000000..a8cd5f4
--- /dev/null
+++ b/doc/guides/sample_app_ug/flow_distributor.rst
@@ -0,0 +1,494 @@
+..  BSD LICENSE
+    Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Flow Distributor Sample Application
+===================================
+
+This sample application demonstrates the use of EFD library as a flow-level
+load balancer, for more information about the EFD Library please refer to the
+DPDK programmer's guide.
+
+This sample application is a variant of the
+:ref:`client-server sample application <multi_process_app>`
+where a specific target node is specified for every and each flow
+(not in a round-robin fashion as the original load balancing sample application).
+
+Overview
+--------
+
+The architecture of the EFD flow-based load balancer sample application is
+presented in the following figure.
+
+.. _figure_efd_sample_app_overview:
+
+.. figure:: img/flow_distributor.*
+
+   Using EFD as a Flow-Level Load Balancer
+
+As shown in :numref:`_figure_efd_sample_app_overview`,
+the sample application consists of a front-end node (distributor)
+using the EFD library to create a load-balancing table for flows,
+for each flow a target backend worker node is specified. The EFD table does not
+store the flow key (unlike a regular hash table), and hence, it can
+individually load-balance millions of flows (number of targets * maximum number
+of flows fit in a flow table per target) while still fitting in CPU cache.
+
+It should be noted that although they are referred to as nodes, the frontend
+distributor and worker nodes are processes running on the same platform.
+
+Front-end Distributor
+~~~~~~~~~~~~~~~~~~~~~
+
+Upon initializing, the frontend distributor node (process) creates a flow
+distributor table (based on the EFD library) which is populated with flow
+information and its intended target node.
+
+The sample application assigns a specific target node_id (process) for each of
+the IP destination addresses as follows:
+
+.. code-block:: c
+
+    node_id = i % num_nodes; /* Target node id is generated */
+    ip_dst = rte_cpu_to_be_32(i); /* Specific ip destination address is
+                                     assigned to this target node */
+
+then the pair of <key,target> is inserted into the flow distribution table.
+
+The main loop of the the distributor node receives a burst of packets, then for
+each packet, a flow key (IP destination address) is extracted. The flow
+distributor table is looked up and the target node id is returned.  Packets are
+then enqueued to the specified target node id.
+
+It should be noted that flow distributor table is not a membership test table.
+I.e. if the key has already been inserted the target node id will be correct,
+but for new keys the flow distributor table will return a value (which can be
+valid).
+
+Backend Worker Nodes
+~~~~~~~~~~~~~~~~~~~~
+
+Upon initializing, the worker node (process) creates a flow table (a regular
+hash table that stores the key default size 1M flows) which is populated with
+only the flow information that is serviced at this node. This flow key is
+essential to point out new keys that have not been inserted before.
+
+The worker node's main loop is simply receiving packets then doing a hash table
+lookup. If a match occurs then statistics are updated for flows serviced by
+this node. If no match is found in the local hash table then this indicates
+that this is a new flow, which is dropped.
+
+
+Compiling the Application
+-------------------------
+
+The sequence of steps used to build the application is:
+
+#.  Export the required environment variables:
+
+    .. code-block:: console
+
+        export RTE_SDK=/path/to/rte_sdk
+        export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+#.  Build the application executable file:
+
+    .. code-block:: console
+
+        cd ${RTE_SDK}/examples/flow_distributor/
+        make
+
+    For more details on how to build the DPDK libraries and sample
+    applications,
+    please refer to the *DPDK Getting Started Guide.*
+
+
+Running the Application
+-----------------------
+
+The application has two binaries to be run: the front-end distributor
+and the back-end node.
+
+The frontend distributor (distributor) has the following command line options::
+
+    ./distributor [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS
+
+Where,
+
+* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
+* ``-n NUM_NODES:`` Number of back-end nodes that will be used
+* ``-f NUM_FLOWS:`` Number of flows to be added in the EFD table (1 million, by default)
+
+The back-end node (node) has the following command line options::
+
+    ./node [EAL options] -- -n NODE_ID
+
+Where,
+
+* ``-n NODE_ID:`` Node ID, which cannot be equal or higher than NUM_MODES
+
+
+First, the distributor app must be launched, with the number of nodes that will be run.
+Once it has been started, the node instances can be run, with different NODE_ID.
+These instances have to be run as secondary processes, with ``--proc-type=secondary``
+in the EAL options, which will attach to the primary process memory, and therefore,
+they can access the queues created by the primary process to distribute packets.
+
+To successfully run the application, the command line used to start the
+application has to be in sync with the traffic flows configured on the traffic
+generator side.
+
+For examples of application command lines and traffic generator flows, please
+refer to the DPDK Test Report. For more details on how to set up and run the
+sample applications provided with DPDK package, please refer to the
+:ref:`DPDK Getting Started Guide for Linux <linux_gsg>` and
+:ref:`DPDK Getting Started Guide for FreeBSD <freebsd_gsg>`.
+
+
+Explanation
+-----------
+
+As described in previous sections, there are two processes in this example.
+
+The first process, the front-end distributor, creates and populates the EFD table,
+which is used to distribute packets to nodes, which the number of flows
+specified in the command line (1 million, by default).
+
+
+.. code-block:: c
+
+    static void
+    create_flow_distributor_table(void)
+    {
+        uint8_t socket_id = rte_socket_id();
+
+        /* create table */
+        efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
+                        1 << socket_id, socket_id);
+
+        if (efd_table == NULL)
+            rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
+    }
+
+    static void
+    populate_flow_distributor_table(void)
+    {
+        unsigned int i;
+        int32_t ret;
+        uint32_t ip_dst;
+        uint8_t socket_id = rte_socket_id();
+        uint64_t node_id;
+
+        /* Add flows in table */
+        for (i = 0; i < num_flows; i++) {
+            node_id = i % num_nodes;
+
+            ip_dst = rte_cpu_to_be_32(i);
+            ret = rte_efd_update(efd_table, socket_id,
+                            (void *)&ip_dst, (efd_value_t)node_id);
+            if (ret < 0)
+                rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
+                                    "flow distributor table\n", i);
+        }
+
+        printf("EFD table: Adding 0x%x keys\n", num_flows);
+    }
+
+After initialization, packets are received from the enabled ports, and the IPv4
+address from the packets is used as a key to look up in the EFD table,
+which tells the node where the packet has to be distributed.
+
+.. code-block:: c
+
+    static void
+    process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
+            uint16_t rx_count, unsigned int socket_id)
+    {
+        uint16_t i;
+        uint8_t node;
+        efd_value_t data[EFD_BURST_MAX];
+        const void *key_ptrs[EFD_BURST_MAX];
+
+        struct ipv4_hdr *ipv4_hdr;
+        uint32_t ipv4_dst_ip[EFD_BURST_MAX];
+
+        for (i = 0; i < rx_count; i++) {
+            /* Handle IPv4 header.*/
+            ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
+                    sizeof(struct ether_hdr));
+            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+            key_ptrs[i] = (void *)&ipv4_dst_ip[i];
+        }
+
+        rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
+                    (const void **) key_ptrs, data);
+        for (i = 0; i < rx_count; i++) {
+            node = (uint8_t) ((uintptr_t)data[i]);
+
+            if (node >= num_nodes) {
+                /*
+                 * Node is out of range, which means that
+                 * flow has not been inserted
+                 */
+                flow_dist_stats.drop++;
+                rte_pktmbuf_free(pkts[i]);
+            } else {
+                flow_dist_stats.distributed++;
+                enqueue_rx_packet(node, pkts[i]);
+            }
+        }
+
+        for (i = 0; i < num_nodes; i++)
+            flush_rx_queue(i);
+    }
+
+The burst of packets received is enqueued in temporary buffers (per node),
+and enqueued in the shared ring between the distributor and the node.
+After this, a new burst of packets is received and this process is
+repeated infinitely.
+
+.. code-block:: c
+
+    static void
+    flush_rx_queue(uint16_t node)
+    {
+        uint16_t j;
+        struct node *cl;
+
+        if (cl_rx_buf[node].count == 0)
+            return;
+
+        cl = &nodes[node];
+        if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
+                cl_rx_buf[node].count) != 0){
+            for (j = 0; j < cl_rx_buf[node].count; j++)
+                rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
+            cl->stats.rx_drop += cl_rx_buf[node].count;
+        } else
+            cl->stats.rx += cl_rx_buf[node].count;
+
+        cl_rx_buf[node].count = 0;
+    }
+
+The second process, the back-end node, receives the packets from the shared
+ring with the distributor and send them out, if they belong to the node.
+
+At initialization, it attaches to the distributor process memory, to have
+access to the shared ring, parameters and statistics.
+
+.. code-block:: c
+
+    rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
+    if (rx_ring == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get RX ring - "
+                "is distributor process running?\n");
+
+    mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+    if (mp == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
+
+    mz = rte_memzone_lookup(MZ_SHARED_INFO);
+    if (mz == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
+    info = mz->addr;
+    tx_stats = &(info->tx_stats[node_id]);
+    filter_stats = &(info->filter_stats[node_id]);
+
+Then, the hash table that contains the flows that will be handled
+by the node is created and populated.
+
+.. code-block:: c
+
+    static struct rte_hash *
+    create_hash_table(const struct shared_info *info)
+    {
+        uint32_t num_flows_node = info->num_flows / info->num_nodes;
+        char name[RTE_HASH_NAMESIZE];
+        struct rte_hash *h;
+
+        /* create table */
+        struct rte_hash_parameters hash_params = {
+            .entries = num_flows_node * 2, /* table load = 50% */
+            .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
+            .socket_id = rte_socket_id(),
+            .hash_func_init_val = 0,
+        };
+
+        snprintf(name, sizeof(name), "hash_table_%d", node_id);
+        hash_params.name = name;
+        h = rte_hash_create(&hash_params);
+
+        if (h == NULL)
+            rte_exit(EXIT_FAILURE,
+                    "Problem creating the hash table for node %d\n",
+                    node_id);
+        return h;
+    }
+
+    static void
+    populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
+    {
+        unsigned int i;
+        int32_t ret;
+        uint32_t ip_dst;
+        uint32_t num_flows_node = 0;
+        uint64_t target_node;
+
+        /* Add flows in table */
+        for (i = 0; i < info->num_flows; i++) {
+            target_node = i % info->num_nodes;
+            if (target_node != node_id)
+                continue;
+
+            ip_dst = rte_cpu_to_be_32(i);
+
+            ret = rte_hash_add_key(h, (void *) &ip_dst);
+            if (ret < 0)
+                rte_exit(EXIT_FAILURE, "Unable to add entry %u "
+                        "in hash table\n", i);
+            else
+                num_flows_node++;
+
+        }
+
+        printf("Hash table: Adding 0x%x keys\n", num_flows_node);
+    }
+
+After initialization, packets are dequeued from the shared ring
+(from the distributor) and, like in the distributor process,
+the IPv4 address from the packets is used as a key to look up in the hash table.
+If there is a hit, packet is stored in a buffer, to be eventually transmitted
+in one of the enabled ports. If key is not there, packet is dropped, since the
+flow is not handled by the node.
+
+.. code-block:: c
+
+    static inline void
+    handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
+    {
+        struct ipv4_hdr *ipv4_hdr;
+        uint32_t ipv4_dst_ip[PKT_READ_SIZE];
+        const void *key_ptrs[PKT_READ_SIZE];
+        unsigned int i;
+        int32_t positions[PKT_READ_SIZE] = {0};
+
+        for (i = 0; i < num_packets; i++) {
+            /* Handle IPv4 header.*/
+            ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
+                    sizeof(struct ether_hdr));
+            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+            key_ptrs[i] = &ipv4_dst_ip[i];
+        }
+        /* Check if packets belongs to any flows handled by this node */
+        rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
+
+        for (i = 0; i < num_packets; i++) {
+            if (likely(positions[i] >= 0)) {
+                filter_stats->passed++;
+                transmit_packet(bufs[i]);
+            } else {
+                filter_stats->drop++;
+                /* Drop packet, as flow is not handled by this node */
+                rte_pktmbuf_free(bufs[i]);
+            }
+        }
+    }
+
+Finally, note that both processes updates statistics, such as transmitted, received
+and dropped packets, which are shown and refreshed by the distributor app.
+
+.. code-block:: c
+
+    static void
+    do_stats_display(void)
+    {
+        unsigned int i, j;
+        const char clr[] = {27, '[', '2', 'J', '\0'};
+        const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+        uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
+        uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
+
+        /* to get TX stats, we need to do some summing calculations */
+        memset(port_tx, 0, sizeof(port_tx));
+        memset(port_tx_drop, 0, sizeof(port_tx_drop));
+        memset(node_tx, 0, sizeof(node_tx));
+        memset(node_tx_drop, 0, sizeof(node_tx_drop));
+
+        for (i = 0; i < num_nodes; i++) {
+            const struct tx_stats *tx = &info->tx_stats[i];
+
+            for (j = 0; j < info->num_ports; j++) {
+                const uint64_t tx_val = tx->tx[info->id[j]];
+                const uint64_t drop_val = tx->tx_drop[info->id[j]];
+
+                port_tx[j] += tx_val;
+                port_tx_drop[j] += drop_val;
+                node_tx[i] += tx_val;
+                node_tx_drop[i] += drop_val;
+            }
+        }
+
+        /* Clear screen and move to top left */
+        printf("%s%s", clr, topLeft);
+
+        printf("PORTS\n");
+        printf("-----\n");
+        for (i = 0; i < info->num_ports; i++)
+            printf("Port %u: '%s'\t", (unsigned int)info->id[i],
+                    get_printable_mac_addr(info->id[i]));
+        printf("\n\n");
+        for (i = 0; i < info->num_ports; i++) {
+            printf("Port %u - rx: %9"PRIu64"\t"
+                    "tx: %9"PRIu64"\n",
+                    (unsigned int)info->id[i], info->rx_stats.rx[i],
+                    port_tx[i]);
+        }
+
+        printf("\nFLOW DISTRIBUTOR\n");
+        printf("-----\n");
+        printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
+                flow_dist_stats.distributed, flow_dist_stats.drop);
+
+        printf("\nNODES\n");
+        printf("-------\n");
+        for (i = 0; i < num_nodes; i++) {
+            const unsigned long long rx = nodes[i].stats.rx;
+            const unsigned long long rx_drop = nodes[i].stats.rx_drop;
+            const struct filter_stats *filter = &info->filter_stats[i];
+
+            printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
+                    "            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
+                    "            filter_passed: %9"PRIu64", "
+                    "filter_drop: %9"PRIu64"\n",
+                    i, rx, rx_drop, node_tx[i], node_tx_drop[i],
+                    filter->passed, filter->drop);
+        }
+
+        printf("\n");
+    }
diff --git a/doc/guides/sample_app_ug/img/flow_distributor.svg b/doc/guides/sample_app_ug/img/flow_distributor.svg
new file mode 100644
index 0000000..9aee30b
--- /dev/null
+++ b/doc/guides/sample_app_ug/img/flow_distributor.svg
@@ -0,0 +1,1254 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i6.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.2496in" height="5.89673in"
+		viewBox="0 0 593.971 424.565" xml:space="preserve" color-interpolation-filters="sRGB" class="st27">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {visibility:visible}
+		.st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st4 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
+		.st5 {fill:#feffff;font-family:Calibri;font-size:0.75em}
+		.st6 {fill:none;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st7 {fill:none;stroke:#2e75b5;stroke-width:2.25}
+		.st8 {fill:#305497;stroke:#2e75b5;stroke-width:1}
+		.st9 {fill:#feffff;font-family:Calibri;font-size:0.833336em;font-weight:bold}
+		.st10 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2)}
+		.st11 {fill:#5b9bd5}
+		.st12 {stroke:#c7c8c8;stroke-width:0.25}
+		.st13 {fill:#acccea;stroke:#c7c8c8;stroke-width:0.25}
+		.st14 {fill:#feffff;font-family:Calibri;font-size:1.00001em;font-weight:bold}
+		.st15 {fill:#ed7d31;stroke:#c7c8c8;stroke-width:0.25}
+		.st16 {fill:#deebf6;stroke:#c7c8c8;stroke-width:0.25}
+		.st17 {marker-end:url(#mrkr5-212);stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st18 {fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1;stroke-width:0.28409090909091}
+		.st19 {fill:none;stroke:#2e75b5;stroke-width:1}
+		.st20 {fill:#5b9bd5;font-family:Calibri;font-size:1.00001em}
+		.st21 {fill:none;stroke:none;stroke-width:0.25}
+		.st22 {font-size:1em}
+		.st23 {fill:#ffffff}
+		.st24 {stroke:#5b9bd5;stroke-dasharray:1.5,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st25 {marker-end:url(#mrkr5-444);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st26 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
+		.st27 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-212" class="st18" v:arrowType="5" v:arrowSize="2" v:setback="5.8" refX="-5.8" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
+		</marker>
+		<marker id="mrkr5-444" class="st26" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+		</marker>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(319.501,-335.688)">
+			<title>Rectangle.58</title>
+			<desc>Key 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow3-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>		</g>
+		<g id="shape4-7" v:mID="4" v:groupContext="shape" transform="translate(353.251,-335.688)">
+			<title>Rectangle.59</title>
+			<desc>Action 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow4-8" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>		</g>
+		<g id="shape5-13" v:mID="5" v:groupContext="shape" transform="translate(400.501,-335.688)">
+			<title>Rectangle.60</title>
+			<desc>Key 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow5-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>		</g>
+		<g id="shape6-19" v:mID="6" v:groupContext="shape" transform="translate(434.251,-335.688)">
+			<title>Rectangle.61</title>
+			<desc>Action 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow6-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>		</g>
+		<g id="shape7-25" v:mID="7" v:groupContext="shape" transform="translate(481.501,-335.688)">
+			<title>Rectangle.62</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow7-26" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape8-30" v:mID="8" v:groupContext="shape" transform="translate(515.251,-335.688)">
+			<title>Rectangle.63</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow8-31" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape9-35" v:mID="9" v:groupContext="shape" transform="translate(319.501,-313.188)">
+			<title>Rectangle.64</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow9-36" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape10-40" v:mID="10" v:groupContext="shape" transform="translate(353.251,-313.188)">
+			<title>Rectangle.65</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow10-41" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape11-45" v:mID="11" v:groupContext="shape" transform="translate(400.501,-313.188)">
+			<title>Rectangle.66</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow11-46" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape12-50" v:mID="12" v:groupContext="shape" transform="translate(434.251,-313.188)">
+			<title>Rectangle.67</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow12-51" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape13-55" v:mID="13" v:groupContext="shape" transform="translate(481.501,-313.188)">
+			<title>Rectangle.68</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow13-56" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape14-60" v:mID="14" v:groupContext="shape" transform="translate(515.251,-313.188)">
+			<title>Rectangle.69</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow14-61" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape15-65" v:mID="15" v:groupContext="shape" transform="translate(319.501,-277.188)">
+			<title>Rectangle.70</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow15-66" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape16-71" v:mID="16" v:groupContext="shape" transform="translate(353.251,-277.188)">
+			<title>Rectangle.71</title>
+			<desc>Action x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow16-72" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>		</g>
+		<g id="shape17-77" v:mID="17" v:groupContext="shape" transform="translate(400.501,-277.188)">
+			<title>Rectangle.72</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow17-78" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape18-83" v:mID="18" v:groupContext="shape" transform="translate(434.251,-277.188)">
+			<title>Rectangle.73</title>
+			<desc>Action y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow18-84" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>		</g>
+		<g id="shape19-89" v:mID="19" v:groupContext="shape" transform="translate(481.501,-277.188)">
+			<title>Rectangle.74</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow19-90" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="shape20-95" v:mID="20" v:groupContext="shape" transform="translate(515.251,-277.188)">
+			<title>Rectangle.75</title>
+			<desc>Action z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow20-96" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>		</g>
+		<g id="shape21-101" v:mID="21" v:groupContext="shape" transform="translate(319.501,-240.687)">
+			<title>Rectangle.76</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow21-102" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape22-106" v:mID="22" v:groupContext="shape" transform="translate(353.251,-240.687)">
+			<title>Rectangle.77</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow22-107" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape23-111" v:mID="23" v:groupContext="shape" transform="translate(400.501,-240.687)">
+			<title>Rectangle.78</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow23-112" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape24-116" v:mID="24" v:groupContext="shape" transform="translate(434.251,-240.687)">
+			<title>Rectangle.79</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow24-117" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape25-121" v:mID="25" v:groupContext="shape" transform="translate(481.501,-240.687)">
+			<title>Rectangle.80</title>
+			<desc>Key N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow25-122" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>		</g>
+		<g id="shape26-127" v:mID="26" v:groupContext="shape" transform="translate(515.251,-240.687)">
+			<title>Rectangle.81</title>
+			<desc>Action N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow26-128" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>		</g>
+		<g id="shape27-133" v:mID="27" v:groupContext="shape" transform="translate(317.251,-231.687)">
+			<title>Rectangle.82</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow27-134" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+			</g>
+			<rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+		</g>
+		<g id="shape28-138" v:mID="28" v:groupContext="shape" transform="translate(328.501,-362.688)">
+			<title>Sheet.28</title>
+			<desc>Local Table for N Specific Flows Serviced at Node 1</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+			<rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+			<text x="5.77" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node 1</text>		</g>
+		<g id="group34-141" transform="translate(66.0294,-165.569)" v:mID="34" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Device)"/>
+				<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Load balancer)"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Load balancer</title>
+			<g id="shape35-142" v:mID="35" v:groupContext="shape" transform="translate(0,-7.33146)">
+				<title>Sheet.35</title>
+				<g id="shadow35-143" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st10"/>
+					<path d="M0 377.86 L72 377.86" class="st6"/>
+					<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st6"/>
+				</g>
+				<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st11"/>
+				<path d="M0 377.86 L72 377.86" class="st12"/>
+				<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st12"/>
+			</g>
+			<g id="shape36-152" v:mID="36" v:groupContext="shape" transform="translate(8.03054,-12.9324)">
+				<title>Sheet.36</title>
+				<g id="shadow36-153" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97
+								 413.34 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83
+								 L51.34 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37
+								 L38.13 400.48 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16
+								 L46.49 392.01 ZM27.71 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42
+								 27.71 415.42 C32.75 415.42 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71
+								 400.04 C31.15 400.04 33.96 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54
+								 21.46 409.74 21.46 406.29 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56
+								 L11.6 407.56 L8.62 410.51 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z"
+							class="st2"/>
+				</g>
+				<path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97 413.34
+							 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83 L51.34
+							 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37 L38.13 400.48
+							 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16 L46.49 392.01 ZM27.71
+							 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42 27.71 415.42 C32.75 415.42
+							 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71 400.04 C31.15 400.04 33.96
+							 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54 21.46 409.74 21.46 406.29
+							 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56 L11.6 407.56 L8.62 410.51
+							 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z" class="st13"/>
+			</g>
+		</g>
+		<g id="shape37-157" v:mID="37" v:groupContext="shape" transform="translate(21.0294,-45.4375)">
+			<title>Rectangle</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow37-158" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="336.433" width="135" height="88.1315" class="st2"/>
+			</g>
+			<rect x="0" y="336.433" width="135" height="88.1315" class="st3"/>
+		</g>
+		<g id="shape38-162" v:mID="38" v:groupContext="shape" transform="translate(34.693,-126.438)">
+			<title>Sheet.38</title>
+			<desc>EFD Table</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="49.3364" cy="415.565" width="98.68" height="18"/>
+			<rect x="0" y="406.565" width="98.6728" height="18" class="st8"/>
+			<text x="24.87" y="419.17" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>EFD Table</text>		</g>
+		<g id="shape39-165" v:mID="39" v:groupContext="shape" transform="translate(30.0294,-99.4375)">
+			<title>Rectangle.39</title>
+			<desc>Group_id</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+			<g id="shadow39-166" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+			<text x="7.87" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group_id</text>		</g>
+		<g id="shape40-171" v:mID="40" v:groupContext="shape" transform="translate(93.193,-99.4375)">
+			<title>Rectangle.40</title>
+			<desc>Hash index</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+			<g id="shadow40-172" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+			<text x="4.64" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Hash index</text>		</g>
+		<g id="shape41-177" v:mID="41" v:groupContext="shape" transform="translate(30.193,-82.4275)">
+			<title>Rectangle.41</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow41-178" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape42-182" v:mID="42" v:groupContext="shape" transform="translate(30.193,-66.8125)">
+			<title>Rectangle.42</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow42-183" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape43-187" v:mID="43" v:groupContext="shape" transform="translate(30.1112,-52.1875)">
+			<title>Rectangle.43</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow43-188" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape44-192" v:mID="44" v:groupContext="shape" transform="translate(93.0294,-81.4375)">
+			<title>Rectangle.44</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow44-193" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape45-197" v:mID="45" v:groupContext="shape" transform="translate(93.193,-66.8125)">
+			<title>Rectangle.45</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow45-198" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape46-202" v:mID="46" v:groupContext="shape" transform="translate(93.193,-52.1875)">
+			<title>Rectangle.46</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow46-203" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape47-207" v:mID="47" v:groupContext="shape" transform="translate(374.924,544.022) rotate(135)">
+			<title>Sheet.47</title>
+			<path d="M-0 417.75 A40.674 18.0151 -156.2 0 0 40.24 422.15 L40.49 421.89" class="st17"/>
+		</g>
+		<g id="shape48-213" v:mID="48" v:groupContext="shape" transform="translate(21.0294,-19)">
+			<title>Sheet.48</title>
+			<desc>Supports X*N Flows</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+			<rect x="0" y="406.565" width="135" height="18" class="st19"/>
+			<text x="19.05" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports X*N Flows</text>		</g>
+		<g id="shape49-216" v:mID="49" v:groupContext="shape" transform="translate(48.0294,-229.938)">
+			<title>Sheet.49</title>
+			<desc>Frontend Server or Load Balancer</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="400.94" width="108" height="47.25"/>
+			<rect x="0" y="377.315" width="108" height="47.25" class="st21"/>
+			<text x="14.56" y="397.34" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Frontend Server<v:newlineChar/><tspan
+						x="13.16" dy="1.2em" class="st22">or Load Balancer </tspan> </text>		</g>
+		<g id="group51-220" transform="translate(223.876,-310.938)" v:mID="51" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server</title>
+			<g id="shape52-221" v:mID="52" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.52</title>
+				<g id="shadow52-222" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape53-226" v:mID="53" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.53</title>
+				<g id="shadow53-227" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape54-231" v:mID="54" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.54</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow54-232" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape59-239" v:mID="59" v:groupContext="shape" transform="translate(277.876,-373.938)">
+			<title>Sheet.59</title>
+			<path d="M-0 424.56 A111.108 53.2538 42.31 0 1 93.83 421.21 L94.14 421.41" class="st17"/>
+		</g>
+		<g id="shape60-244" v:mID="60" v:groupContext="shape" transform="translate(205.876,-283.938)">
+			<title>Sheet.60</title>
+			<desc>Backend Server 1</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 1</text>		</g>
+		<g id="group61-247" transform="translate(223.876,-207.438)" v:mID="61" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server.61</title>
+			<g id="shape62-248" v:mID="62" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.62</title>
+				<g id="shadow62-249" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape63-253" v:mID="63" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.63</title>
+				<g id="shadow63-254" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape64-258" v:mID="64" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.64</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow64-259" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape65-266" v:mID="65" v:groupContext="shape" transform="translate(205.876,-180.437)">
+			<title>Sheet.65</title>
+			<desc>Backend Server 2</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 2</text>		</g>
+		<g id="group66-269" transform="translate(219.029,-58.9375)" v:mID="66" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server.66</title>
+			<g id="shape67-270" v:mID="67" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.67</title>
+				<g id="shadow67-271" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape68-275" v:mID="68" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.68</title>
+				<g id="shadow68-276" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape69-280" v:mID="69" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.69</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow69-281" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape70-288" v:mID="70" v:groupContext="shape" transform="translate(201.029,-26.056)">
+			<title>Sheet.70</title>
+			<desc>Backend Server X</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.86" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server X</text>		</g>
+		<g id="shape71-291" v:mID="71" v:groupContext="shape" transform="translate(684.44,239.627) rotate(90)">
+			<title>Sheet.71</title>
+			<path d="M0 424.56 L45 424.56" class="st24"/>
+		</g>
+		<g id="shape72-294" v:mID="72" v:groupContext="shape" transform="translate(6.85967,-22.443) rotate(-38.1076)">
+			<title>Sheet.72</title>
+			<path d="M-0 424.56 A96.1331 44.4001 55.03 0 1 68.24 420.56 L68.51 420.79" class="st17"/>
+		</g>
+		<g id="shape73-299" v:mID="73" v:groupContext="shape" transform="translate(328.501,-135.937)">
+			<title>Rectangle.73</title>
+			<desc>Key 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow73-300" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>		</g>
+		<g id="shape74-305" v:mID="74" v:groupContext="shape" transform="translate(362.251,-135.937)">
+			<title>Rectangle.74</title>
+			<desc>Action 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow74-306" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>		</g>
+		<g id="shape75-311" v:mID="75" v:groupContext="shape" transform="translate(409.501,-135.937)">
+			<title>Rectangle.75</title>
+			<desc>Key 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow75-312" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>		</g>
+		<g id="shape76-317" v:mID="76" v:groupContext="shape" transform="translate(443.251,-135.937)">
+			<title>Rectangle.76</title>
+			<desc>Action 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow76-318" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>		</g>
+		<g id="shape77-323" v:mID="77" v:groupContext="shape" transform="translate(490.501,-135.937)">
+			<title>Rectangle.77</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow77-324" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape78-328" v:mID="78" v:groupContext="shape" transform="translate(524.251,-135.937)">
+			<title>Rectangle.78</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow78-329" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape79-333" v:mID="79" v:groupContext="shape" transform="translate(328.501,-113.437)">
+			<title>Rectangle.79</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow79-334" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape80-338" v:mID="80" v:groupContext="shape" transform="translate(362.251,-113.437)">
+			<title>Rectangle.80</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow80-339" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape81-343" v:mID="81" v:groupContext="shape" transform="translate(409.501,-113.437)">
+			<title>Rectangle.81</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow81-344" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape82-348" v:mID="82" v:groupContext="shape" transform="translate(443.251,-113.437)">
+			<title>Rectangle.82</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow82-349" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape83-353" v:mID="83" v:groupContext="shape" transform="translate(490.501,-113.437)">
+			<title>Rectangle.83</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow83-354" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape84-358" v:mID="84" v:groupContext="shape" transform="translate(524.251,-113.437)">
+			<title>Rectangle.84</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow84-359" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape85-363" v:mID="85" v:groupContext="shape" transform="translate(328.501,-77.4375)">
+			<title>Rectangle.85</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow85-364" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape86-369" v:mID="86" v:groupContext="shape" transform="translate(362.251,-77.4375)">
+			<title>Rectangle.86</title>
+			<desc>Action x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow86-370" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>		</g>
+		<g id="shape87-375" v:mID="87" v:groupContext="shape" transform="translate(409.501,-77.4375)">
+			<title>Rectangle.87</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow87-376" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape88-381" v:mID="88" v:groupContext="shape" transform="translate(443.251,-77.4375)">
+			<title>Rectangle.88</title>
+			<desc>Action y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow88-382" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>		</g>
+		<g id="shape89-387" v:mID="89" v:groupContext="shape" transform="translate(490.501,-77.4375)">
+			<title>Rectangle.89</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow89-388" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="shape90-393" v:mID="90" v:groupContext="shape" transform="translate(524.251,-77.4375)">
+			<title>Rectangle.90</title>
+			<desc>Action z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow90-394" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>		</g>
+		<g id="shape91-399" v:mID="91" v:groupContext="shape" transform="translate(328.501,-40.9375)">
+			<title>Rectangle.91</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow91-400" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape92-404" v:mID="92" v:groupContext="shape" transform="translate(362.251,-40.9375)">
+			<title>Rectangle.92</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow92-405" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape93-409" v:mID="93" v:groupContext="shape" transform="translate(409.501,-40.9375)">
+			<title>Rectangle.93</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow93-410" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape94-414" v:mID="94" v:groupContext="shape" transform="translate(443.251,-40.9375)">
+			<title>Rectangle.94</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow94-415" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape95-419" v:mID="95" v:groupContext="shape" transform="translate(490.501,-40.9375)">
+			<title>Rectangle.95</title>
+			<desc>Key N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow95-420" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>		</g>
+		<g id="shape96-425" v:mID="96" v:groupContext="shape" transform="translate(524.251,-40.9375)">
+			<title>Rectangle.96</title>
+			<desc>Action N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow96-426" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>		</g>
+		<g id="shape97-431" v:mID="97" v:groupContext="shape" transform="translate(326.251,-31.9375)">
+			<title>Rectangle.97</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow97-432" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+			</g>
+			<rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+		</g>
+		<g id="shape98-436" v:mID="98" v:groupContext="shape" transform="translate(337.501,-162.938)">
+			<title>Sheet.98</title>
+			<desc>Local Table for N Specific Flows Serviced at Node X</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+			<rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+			<text x="5.55" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node X</text>		</g>
+		<g id="shape99-439" v:mID="99" v:groupContext="shape" transform="translate(-204.342,-29.4449) rotate(-53.7462)">
+			<title>Sheet.99</title>
+			<path d="M0 424.56 L160.37 424.56" class="st25"/>
+		</g>
+		<g id="shape100-445" v:mID="100" v:groupContext="shape" transform="translate(-37.6568,-164.882) rotate(-24.444)">
+			<title>Sheet.100</title>
+			<path d="M0 424.56 L101.71 424.56" class="st25"/>
+		</g>
+		<g id="shape101-450" v:mID="101" v:groupContext="shape" transform="translate(464.049,-50.8578) rotate(50.099)">
+			<title>Sheet.101</title>
+			<path d="M0 424.56 L139.8 424.56" class="st25"/>
+		</g>
+		<g id="shape102-455" v:mID="102" v:groupContext="shape" transform="translate(372.376,-207.438)">
+			<title>Sheet.102</title>
+			<desc>Supports N Flows</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+			<rect x="0" y="406.565" width="135" height="18" class="st19"/>
+			<text x="25.15" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports N Flows</text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 775e2f7..260f6a5 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -57,6 +57,7 @@ Sample Applications User Guides
     l3_forward_virtual
     link_status_intr
     load_balancer
+    flow_distributor
     multi_process
     qos_metering
     qos_scheduler
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 4/5] doc: add EFD library section in Programmers guide
From: Pablo de Lara @ 2017-01-15 12:04 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara, Sameh Gobriel
In-Reply-To: <1484481875-126335-1-git-send-email-pablo.de.lara.guarch@intel.com>

Signed-off-by: Sameh Gobriel <sameh.gobriel@intel.com>
Acked-by: Christian Maciocco <christian.maciocco@intel.com>
---
 MAINTAINERS                            |    1 +
 doc/guides/prog_guide/efd_lib.rst      |  438 +++++++++++
 doc/guides/prog_guide/img/efd_i1.svg   |  130 ++++
 doc/guides/prog_guide/img/efd_i10.svg  |  384 ++++++++++
 doc/guides/prog_guide/img/efd_i11.svg  |  319 ++++++++
 doc/guides/prog_guide/img/efd_i12.svg  | 1008 +++++++++++++++++++++++++
 doc/guides/prog_guide/img/efd_i2.svg   |  280 +++++++
 doc/guides/prog_guide/img/efd_i3.svg   |  634 ++++++++++++++++
 doc/guides/prog_guide/img/efd_i4.svg   |  203 ++++++
 doc/guides/prog_guide/img/efd_i5.svg   |  183 +++++
 doc/guides/prog_guide/img/efd_i6.svg   | 1254 ++++++++++++++++++++++++++++++++
 doc/guides/prog_guide/img/efd_i7.svg   |  790 ++++++++++++++++++++
 doc/guides/prog_guide/img/efd_i8.svg   |  182 +++++
 doc/guides/prog_guide/img/efd_i9.svg   |  390 ++++++++++
 doc/guides/prog_guide/index.rst        |   23 +
 doc/guides/rel_notes/release_17_02.rst |    3 +
 16 files changed, 6222 insertions(+)
 create mode 100644 doc/guides/prog_guide/efd_lib.rst
 create mode 100644 doc/guides/prog_guide/img/efd_i1.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i10.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i11.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i12.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i2.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i3.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i4.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i5.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i6.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i7.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i8.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i9.svg

diff --git a/MAINTAINERS b/MAINTAINERS
index b124f6e..66e9466 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -532,6 +532,7 @@ EFD
 M: Byron Marohn <byron.marohn@intel.com>
 M: Pablo de Lara Guarch <pablo.de.lara.guarch@intel.com>
 F: lib/librte_efd/
+F: doc/guides/prog_guide/efd_lib.rst
 F: app/test/test_efd*
 F: examples/flow_distributor/
 
diff --git a/doc/guides/prog_guide/efd_lib.rst b/doc/guides/prog_guide/efd_lib.rst
new file mode 100644
index 0000000..c489ae4
--- /dev/null
+++ b/doc/guides/prog_guide/efd_lib.rst
@@ -0,0 +1,438 @@
+..  BSD LICENSE
+    Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+.. _Efd_Library:
+
+Elastic Flow Distributor Library
+================================
+
+Introduction
+------------
+
+In Data Centers today, clustering and scheduling of distributed workloads
+is a very common task. Many workloads require a deterministic
+partitioning of a flat key space among a cluster of machines. When a
+packet enters the cluster, the ingress node will direct the packet to
+its handling node. For example, data-centers with disaggregated storage
+use storage metadata tables to forward I/O requests to the correct back end
+storage cluster, stateful packet inspection will use match incoming
+flows to signatures in flow tables to send incoming packets to their
+intended deep packet inspection (DPI) devices, and so on.
+
+EFD is a distributor library that uses perfect hashing to determine a
+target/value for a given incoming flow key. It has the following
+advantages: first, because it uses perfect hashing it does not store the
+key itself and hence lookup performance is not dependent on the key
+size. Second, the target/value can be any arbitrary value hence the
+system designer and/or operator can better optimize service rates and
+inter-cluster network traffic locating. Third, since the storage
+requirement is much smaller than a hash-based flow table (i.e. better
+fit for CPU cache), EFD can scale to millions of flow keys. Finally,
+with the current optimized library implementation, performance is fully
+scalable with any number of CPU cores.
+
+Flow Based Distribution
+-----------------------
+
+Computation Based Schemes
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Flow distribution and/or load balancing can be simply done using a
+stateless computation, for instance using round-robin or a simple
+computation based on the flow key as an input. For example, a hash
+function can be used to direct a certain flow to a target based on
+the flow key (e.g. ``h(key) mod n``) where h(key) is the hash value of the
+flow key and n is the number of possible targets.
+
+.. _figure_efd1:
+
+.. figure:: img/efd_i1.*
+
+  Load Balancing Using Front End Node
+
+In this scheme (:numref:`figure_efd1`), the front end server/distributor/load balancer
+extracts the flow key from the input packet and applies a computation to determine where
+this flow should be directed. Intuitively, this scheme is very simple
+and requires no state to be kept at the front end node, and hence,
+storage requirements are minimum.
+
+.. _figure_efd2:
+
+.. figure:: img/efd_i2.*
+
+  Consistent Hashing
+
+A widely used flow distributor that belongs to the same category of
+computation-based schemes is ``consistent hashing``, shown in :numref:`figure_efd2`.
+Target destinations (shown in red) are hashed into the same space as the flow
+keys (shown in blue), and keys are mapped to the nearest target in a clockwise
+fashion. Dynamically adding and removing targets with consistent hashing
+requires only K/n keys to be remapped on average, where K is the number of
+keys, and n is the number of targets. In contrast, in a traditional hash-based
+scheme, a change in the number of targets causes nearly all keys to be
+remapped.
+
+Although computation-based schemes are simple and need very little
+storage requirement, they suffer from the drawback that the system
+designer/operator can’t fully control the target to assign a specific
+key, as this is dictated by the hash function.
+Deterministically co-locating of keys together (for example, to minimize
+inter-server traffic or to optimize for network traffic conditions,
+target load, etc.) is simply not possible.
+
+Flow-Table Based Schemes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+When using a Flow-Table based scheme to handle flow distribution/load
+balancing, in contrast with computation-based schemes, the system designer
+has the flexibility of assigning a given flow to any given
+target. The flow table (e.g. DPDK RTE Hash Library) will simply store
+both the flow key and the target value.
+
+.. _figure_efd3:
+
+.. figure:: img/efd_i3.*
+
+  Table Based Flow Distribution
+
+As shown in :numref:`figure_efd3`, when doing a lookup, the flow-table
+is indexed with the hash of the flow key and the keys (more than one is possible,
+because of hash collision) stored in this index and corresponding values
+are retrieved. The retrieved key(s) is matched with the input flow key
+and if there is a match the value (target id) is returned.
+
+The drawback of using a hash table for flow distribution/load balancing
+is the storage requirement, since the flow table need to store keys,
+signatures and target values. This doesn't allow this scheme to scale to
+millions of flow keys. Large tables will usually not fit in
+the CPU cache, and hence, the lookup performance is degraded because of
+the latency to access the main memory.
+
+EFD Based Scheme
+~~~~~~~~~~~~~~~~
+
+EFD combines the advantages of both flow-table based and computation-based
+schemes. It doesn't require the large storage necessary for
+flow-table based schemes (because EFD doesn't store the key as explained
+below), and it supports any arbitrary value for any given key.
+
+.. _figure_efd4:
+
+.. figure:: img/efd_i4.*
+
+  Searching for Perfect Hash Function
+
+The basic idea of EFD is when a given key is to be inserted, a family of
+hash functions is searched until the correct hash function that maps the
+input key to the correct value is found, as shown in :numref:`figure_efd4`.
+However, rather than explicitly storing all keys and their associated values,
+EFD stores only indices of hash functions that map keys to values, and
+thereby consumes much less space than conventional flow-based tables.
+The lookup operation is very simple, similar to a computational-based
+scheme: given an input key the lookup operation is reduced to hashing
+that key with the correct hash function.
+
+.. _figure_efd5:
+
+.. figure:: img/efd_i5.*
+
+  Divide and Conquer for Millions of Keys
+
+Intuitively, finding a hash function that maps each of a large number
+(millions) of input keys to the correct output value is effectively
+impossible, as a result EFD, as shown in :numref:`figure_efd5`,
+breaks the problem into smaller pieces (divide and conquer).
+EFD divides the entire input key set into many small groups.
+Each group consists of approximately 20-28 keys (a configurable parameter
+for the library), then, for each small group, a brute force search to find
+a hash function that produces the correct outputs for each key in the group.
+
+It should be mentioned that, since the online lookup table for EFD
+doesn't store the key itself, the size of the EFD table is independent
+of the key size and hence EFD lookup performance which is almost
+constant irrespective of the length of the key which is a highly
+desirable feature especially for longer keys.
+
+In summary, EFD is a set separation data structure that supports millions of
+keys. It is used to distribute a given key to an intended target. By itself
+EFD is not a FIB data structure with an exact match the input flow key.
+
+.. _Efd_example:
+
+Example of EFD Library Usage
+----------------------------
+
+EFD can be used along the data path of many network functions and middleboxes.
+As previously mentioned, it can used as an index table for
+<key,value> pairs, meta-data for objects, a flow-level load balancer, etc.
+:numref:`figure_efd6` shows an example of using EFD as a flow-level load
+balancer, where flows are received at a front end server before being forwarded
+to the target back end server for processing. The system designer would
+deterministically co-locate flows together in order to minimize cross-server
+interaction.
+(For example, flows requesting certain webpage objects are co-located
+together, to minimize forwarding of common objects across servers).
+
+.. _figure_efd6:
+
+.. figure:: img/efd_i6.*
+
+  EFD as a Flow-Level Load Balancer
+
+As shown in :numref:`figure_efd6`, the front end server will have an EFD table that
+stores for each group what is the perfect hash index that satisfies the
+correct output. Because the table size is small and fits in cache (since
+keys are not stored), it sustains a large number of flows (N*X, where N
+is the maximum number of flows served by each back end server of the X
+possible targets).
+
+With an input flow key, the group id is computed (for example, using
+last few bits of CRC hash) and then the EFD table is indexed with the
+group id to retrieve the corresponding hash index to use. Once the index
+is retrieved the key is hashed using this hash function and the result
+will be the intended correct target where this flow is supposed to be
+processed.
+
+It should be noted that as a result of EFD not matching the exact key but
+rather distributing the flows to a target back end node based on the
+perfect hash index, a key that has not been inserted before
+will be distributed to a valid target. Hence, a local table which stores
+the flows served at each node is used and is
+exact matched with the input key to rule out new never seen before
+flows.
+
+.. _Efd_api:
+
+Library API Overview
+--------------------
+
+The EFD library API is created with a very similar semantics of a
+hash-index or a flow table. The application creates an EFD table for a
+given maximum number of flows, a function is called to insert a flow key
+with a specific target value, and another function is used to retrieve
+target values for a given individual flow key or a bulk of keys.
+
+EFD Table Create
+~~~~~~~~~~~~~~~~
+
+The function ``rte_efd_create()`` is used to create and return a pointer
+to an EFD table that is sized to hold up to num_flows key.
+The online version of the EFD table (the one that does
+not store the keys and is used for lookups) will be allocated and
+created in the last level cache (LLC) of the socket defined by the
+online_socket_bitmask, while the offline EFD table (the one that
+stores the keys and is used for key inserts and for computing the
+perfect hashing) is allocated and created in the LLC of the socket
+defined by offline_socket_bitmask. It should be noted, that for
+highest performance the socket id should match that where the thread is
+running, i.e. the online EFD lookup table should be created on the same
+socket as where the lookup thread is running.
+
+EFD Insert and Update
+~~~~~~~~~~~~~~~~~~~~~
+
+The EFD function to insert a key or update a key to a new value is
+``rte_efd_update()``. This function will update an existing key to
+a new value (target) if the key has already been inserted
+before, or will insert the <key,value> pair if this key has not been inserted
+before. It will return 0 upon success. It will return
+``EFD_UPDATE_WARN_GROUP_FULL (1)`` if the operation is insert, and the
+last available space in the key's group was just used. It will return
+``EFD_UPDATE_FAILED (2)`` when the insertion or update has failed (either it
+failed to find a suitable perfect hash or the group was full). The function
+will return ``EFD_UPDATE_NO_CHANGE (3)`` if there is no change to the EFD
+table (i.e, same value already exists).
+
+EFD Lookup
+~~~~~~~~~~
+
+To lookup a certain key in an EFD table, the function ``rte_efd_lookup()``
+is used to return the value associated with single key.
+As previously mentioned, if the key has been inserted, the correct value
+inserted is returned, if the key has not been inserted before,
+a ‘random’ value (based on hashing of the key) is returned.
+For better performance and to decrease the overhead of
+function calls per key, it is always recommended to use a bulk lookup
+function (simultaneous lookup of multiple keys) instead of a single key
+lookup function. ``rte_efd_lookup_bulk()`` is the bulk lookup function,
+that looks up num_keys simultaneously stored in the key_list and the
+corresponding return values will be returned in the value_list.
+
+EFD Delete
+~~~~~~~~~~
+
+To delete a certain key in an EFD table, the function
+``rte_efd_delete()`` can be used. The function returns zero upon success 
+when the key has been found and deleted. Socket_id is the parameter to
+use to lookup the existing value, which is ideally the caller's socket id.
+The previous value associated with this key will be returned
+in the prev_value argument.
+
+.. _Efd_internals:
+
+Library Internals
+-----------------
+
+This section provides the brief high-level idea and an overview
+of the library internals to accompany the RFC. The intent of this
+section is to explain to readers the high-level implementation of
+insert, lookup and group rebalancing in the EFD library.
+
+Insert Function Internals
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As previously mentioned the EFD divides the whole set of keys into
+groups of a manageable size (e.g. 28 keys) and then searches for the
+perfect hash that satisfies the intended target value for each key. EFD
+stores two version of the <key,value> table:
+
+-  Offline Version (in memory): Only used for the insertion/update
+   operation, which is less frequent than the lookup operation. In the
+   offline version the exact keys for each group is stored. When a new
+   key is added, the hash function is updated that will satisfy the
+   value for the new key together with the all old keys already inserted
+   in this group.
+
+-  Online Version (in cache): Used for the frequent lookup operation. In
+   the online version, as previously mentioned, the keys are not stored
+   but rather only the hash index for each group.
+
+.. _figure_efd7:
+
+.. figure:: img/efd_i7.*
+
+  Group Assignment
+
+:numref:`figure_efd7` depicts the group assignment for 7 flow keys as an example.
+Given a flow key, a hash function (in our implementation CRC hash) is
+used to get the group id. As shown in the figure, the groups can be
+unbalanced. (We highlight group rebalancing further below).
+
+.. _figure_efd8:
+
+.. figure:: img/efd_i8.*
+
+  Perfect Hash Search - Assigned Keys & Target Value
+
+Focusing on one group that has four keys, :numref:`figure_efd8` depicts the search
+algorithm to find the perfect hash function. Assuming that the target
+value bit for the keys is as shown in the figure, then the online EFD
+table will store a 16 bit hash index and 16 bit lookup table per group
+per value bit.
+
+.. _figure_efd9:
+
+.. figure:: img/efd_i9.*
+
+  Perfect Hash Search - Satisfy Target Values
+
+For a given keyX, a hash function ``(h(keyX, seed1) + index * h(keyX, seed2))``
+is used to point to certain bit index in the 16bit lookup_table value,
+as shown in :numref:`figure_efd9`.
+The insert function will brute force search for all possible values for the
+hash index until a non conflicting lookup_table is found.
+
+.. _figure_efd10:
+
+.. figure:: img/efd_i10.*
+
+  Finding Hash Index for Conflict Free lookup_table
+
+For example, since both key3 and key7 have a target bit value of 1, it
+is okay if the hash function of both keys point to the same bit in the
+lookup table. A conflict will occur if a hash index is used that maps
+both Key4 and Key7 to the same index in the lookup_table,
+as shown in :numref:`figure_efd10`, since their target value bit are not the same.
+Once a hash index is found that produces a lookup_table with no
+contradictions, this index is stored for this group. This procedure is
+repeated for each bit of target value.
+
+Lookup Function Internals
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The design principle of EFD is that lookups are much more frequent than
+inserts, and hence, EFD's design optimizes for the lookups which are
+faster and much simpler than the slower insert procedure (inserts are
+slow, because of perfect hash search as previously discussed).
+
+.. _figure_efd11:
+
+.. figure:: img/efd_i11.*
+
+  EFD Lookup Operation
+
+:numref:`figure_efd11` depicts the lookup operation for EFD. Given an input key,
+the group id is computed (using CRC hash) and then the hash index for this
+group is retrieved from the EFD table. Using the retrieved hash index,
+the hash function ``h(key, seed1) + index *h(key, seed2)`` is used which will
+result in an index in the lookup_table, the bit corresponding to this
+index will be the target value bit. This procedure is repeated for each
+bit of the target value.
+
+Group Rebalancing Function Internals
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When discussing EFD inserts and lookups, the discussion is simplified by
+assuming that a group id is simply a result of hash function. However,
+since hashing in general is not perfect and will not always produce a
+uniform output, this simplified assumption will lead to unbalanced
+groups, i.e., some group will have more keys than other groups.
+Typically, and to minimize insert time with an increasing number of keys,
+it is preferable that all groups will have a balanced number of keys, so
+the brute force search for the perfect hash terminates with a valid hash
+index. In order to achieve this target, groups are rebalanced during
+runtime inserts, and keys are moved around from a busy group to a less
+crowded group as the more keys are inserted.
+
+.. _figure_efd12:
+
+.. figure:: img/efd_i12.*
+
+  Runtime Group Rebalancing
+
+:numref:`figure_efd12` depicts the high level idea of group rebalancing, given an
+input key the hash result is split into two parts a chunk id and 8-bit
+bin id. A chunk contains 64 different groups and 256 bins (i.e. for any
+given bin it can map to 4 distinct groups). When a key is inserted, the
+bin id is computed, for example in :numref:`figure_efd12` bin_id=2,
+and since each bin can be mapped to one of four different groups (2 bit storage),
+the four possible mappings are evaluated and the one that will result in a
+balanced key distribution across these four is selected the mapping result
+is stored in these two bits.
+
+
+References
+-----------
+
+1- EFD is based on collaborative research work between Intel and
+Carnegie Mellon University (CMU), interested readers can refer to the paper
+“Scaling Up Clustered Network Appliances with ScaleBricks;” Dong Zhou et al.
+at SIGCOMM 2015 (`http://conferences.sigcomm.org/sigcomm/2015/pdf/papers/p241.pdf`)
+for more information.
diff --git a/doc/guides/prog_guide/img/efd_i1.svg b/doc/guides/prog_guide/img/efd_i1.svg
new file mode 100644
index 0000000..7f8fcb3
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i1.svg
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i1.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="3.25609in" height="3.375in"
+		viewBox="0 0 234.439 243" xml:space="preserve" color-interpolation-filters="sRGB" class="st10">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {visibility:visible}
+		.st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st4 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
+		.st5 {marker-end:url(#mrkr5-12);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:6}
+		.st6 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.70422535211268}
+		.st7 {stroke:#5b9bd5;stroke-dasharray:2.25,4.5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.25}
+		.st8 {marker-end:url(#mrkr5-39);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st9 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
+		.st10 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-12" class="st6" v:arrowType="5" v:arrowSize="2" v:setback="2.485" refX="-2.485" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-1.42,-1.42) "/>
+		</marker>
+		<marker id="mrkr5-39" class="st9" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+		</marker>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape2-1" v:mID="2" v:groupContext="shape" transform="translate(77.718,-113.348)">
+			<title>Square</title>
+			<desc>LB</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="18" cy="225" width="36" height="36"/>
+			<g id="shadow2-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="207" width="36" height="36" class="st2"/>
+			</g>
+			<rect x="0" y="207" width="36" height="36" class="st3"/>
+			<text x="13.18" y="228" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>LB</text>		</g>
+		<g id="shape3-7" v:mID="3" v:groupContext="shape" transform="translate(37.0513,-131.348)">
+			<title>Sheet.3</title>
+			<path d="M0 243 L25.76 243" class="st5"/>
+		</g>
+		<g id="shape4-13" v:mID="4" v:groupContext="shape" transform="translate(167.718,-178.598)">
+			<title>Square.4</title>
+			<desc>Target 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="22.5" cy="220.5" width="45" height="45"/>
+			<g id="shadow4-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="198" width="45" height="45" class="st2"/>
+			</g>
+			<rect x="0" y="198" width="45" height="45" class="st3"/>
+			<text x="5.74" y="223.5" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Target 1</text>		</g>
+		<g id="shape5-19" v:mID="5" v:groupContext="shape" transform="translate(167.718,-121.005)">
+			<title>Square.5</title>
+			<desc>Target 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="22.5" cy="220.5" width="45" height="45"/>
+			<g id="shadow5-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="198" width="45" height="45" class="st2"/>
+			</g>
+			<rect x="0" y="198" width="45" height="45" class="st3"/>
+			<text x="5.74" y="223.5" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Target 2</text>		</g>
+		<g id="shape7-25" v:mID="7" v:groupContext="shape" transform="translate(167.718,-23.3478)">
+			<title>Square.7</title>
+			<desc>Target N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="22.5" cy="220.5" width="45" height="45"/>
+			<g id="shadow7-26" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="198" width="45" height="45" class="st2"/>
+			</g>
+			<rect x="0" y="198" width="45" height="45" class="st3"/>
+			<text x="5.05" y="223.5" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Target N</text>		</g>
+		<g id="shape8-31" v:mID="8" v:groupContext="shape" transform="translate(433.218,132.402) rotate(90)">
+			<title>Sheet.8</title>
+			<path d="M0 243 L34.59 243" class="st7"/>
+		</g>
+		<g id="shape9-34" v:mID="9" v:groupContext="shape" transform="translate(-78.4279,-37.1059) rotate(-52.2532)">
+			<title>Sheet.9</title>
+			<path d="M0 243 L81.18 243" class="st8"/>
+		</g>
+		<g id="shape11-40" v:mID="11" v:groupContext="shape" transform="translate(60.3469,-125.414) rotate(-12.6875)">
+			<title>Sheet.11</title>
+			<path d="M0 243 L48.32 243" class="st8"/>
+		</g>
+		<g id="shape12-45" v:mID="12" v:groupContext="shape" transform="translate(319.172,-18.1081) rotate(57.7244)">
+			<title>Sheet.12</title>
+			<path d="M0 243 L94.09 243" class="st8"/>
+		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i10.svg b/doc/guides/prog_guide/img/efd_i10.svg
new file mode 100644
index 0000000..d26ec61
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i10.svg
@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i11.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="9.76715in" height="2.82917in"
+		viewBox="0 0 703.234 203.701" xml:space="preserve" color-interpolation-filters="sRGB" class="st15">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {fill:#ffffff;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st2 {fill:none;stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st3 {stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st4 {fill:#000000;font-family:Arial;font-size:0.998566em}
+		.st5 {fill:#0071c5;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st6 {fill:#000000;font-family:Arial;font-size:0.918686em;font-style:italic}
+		.st7 {fill:#000000;font-family:Arial;font-size:0.918686em}
+		.st8 {fill:#7e8d96;font-family:Arial;font-size:0.998566em;font-weight:bold}
+		.st9 {fill:#00b050;font-family:Arial;font-size:0.998566em;font-weight:bold}
+		.st10 {fill:#ff0000;font-family:Arial;font-size:0.998566em;font-weight:bold}
+		.st11 {fill:#004280;stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0299855}
+		.st12 {fill:#ffffff;font-family:Arial;font-size:1.49785em}
+		.st13 {stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st14 {fill:#004280;stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0149927}
+		.st15 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(19.0195,-93.4328)">
+			<title>Sheet.3</title>
+			<path d="M0 182.93 C0 180.64 1.87 178.78 4.16 178.78 L109.18 178.78 C111.47 178.78 113.33 180.64 113.33 182.93 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.16 203.7 C1.87 203.7 0 201.84 0 199.55 L0 182.93 Z"
+					class="st1"/>
+		</g>
+		<g id="shape4-3" v:mID="4" v:groupContext="shape" transform="translate(19.0195,-93.4328)">
+			<title>Sheet.4</title>
+			<path d="M0 182.93 C0 180.64 1.87 178.78 4.16 178.78 L109.18 178.78 C111.47 178.78 113.33 180.64 113.33 182.93 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.16 203.7 C1.87 203.7 0 201.84 0 199.55 L0 182.93 Z"
+					class="st2"/>
+		</g>
+		<g id="shape5-5" v:mID="5" v:groupContext="shape" transform="translate(19.0195,-96.9057)">
+			<title>Sheet.5</title>
+			<desc>Key1: Value = 0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="58.0575" cy="196.509" width="116.12" height="14.3829"/>
+			<path d="M116.11 189.32 L0 189.32 L0 203.7 L116.11 203.7 L116.11 189.32" class="st3"/>
+			<text x="15.59" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key1: Value = 0</text>		</g>
+		<g id="shape6-9" v:mID="6" v:groupContext="shape" transform="translate(19.0195,-68.6284)">
+			<title>Sheet.6</title>
+			<path d="M0 182.93 C0 180.64 1.87 178.78 4.16 178.78 L109.18 178.78 C111.47 178.78 113.33 180.64 113.33 182.93 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.16 203.7 C1.87 203.7 0 201.84 0 199.55 L0 182.93 Z"
+					class="st1"/>
+		</g>
+		<g id="shape7-11" v:mID="7" v:groupContext="shape" transform="translate(19.0195,-68.6284)">
+			<title>Sheet.7</title>
+			<path d="M0 182.93 C0 180.64 1.87 178.78 4.16 178.78 L109.18 178.78 C111.47 178.78 113.33 180.64 113.33 182.93 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.16 203.7 C1.87 203.7 0 201.84 0 199.55 L0 182.93 Z"
+					class="st2"/>
+		</g>
+		<g id="shape8-13" v:mID="8" v:groupContext="shape" transform="translate(19.0195,-72.0832)">
+			<title>Sheet.8</title>
+			<desc>Key3: Value = 1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="58.0575" cy="196.509" width="116.12" height="14.3829"/>
+			<path d="M116.11 189.32 L0 189.32 L0 203.7 L116.11 203.7 L116.11 189.32" class="st3"/>
+			<text x="15.59" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key3: Value = 1</text>		</g>
+		<g id="shape9-17" v:mID="9" v:groupContext="shape" transform="translate(19.0195,-43.5843)">
+			<title>Sheet.9</title>
+			<path d="M0 182.84 C-0 180.53 1.88 178.66 4.19 178.66 L109.15 178.66 C111.46 178.66 113.33 180.53 113.33 182.84 L113.33
+						 199.53 C113.33 201.84 111.46 203.7 109.15 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.53 L0 182.84 Z"
+					class="st1"/>
+		</g>
+		<g id="shape10-19" v:mID="10" v:groupContext="shape" transform="translate(19.0195,-43.5843)">
+			<title>Sheet.10</title>
+			<path d="M0 182.84 C-0 180.53 1.88 178.66 4.19 178.66 L109.15 178.66 C111.46 178.66 113.33 180.53 113.33 182.84 L113.33
+						 199.53 C113.33 201.84 111.46 203.7 109.15 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.53 L0 182.84 Z"
+					class="st2"/>
+		</g>
+		<g id="shape11-21" v:mID="11" v:groupContext="shape" transform="translate(19.0195,-47.1109)">
+			<title>Sheet.11</title>
+			<desc>Key4: Value = 0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="58.0575" cy="196.509" width="116.12" height="14.3829"/>
+			<path d="M116.11 189.32 L0 189.32 L0 203.7 L116.11 203.7 L116.11 189.32" class="st3"/>
+			<text x="15.59" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key4: Value = 0</text>		</g>
+		<g id="shape12-25" v:mID="12" v:groupContext="shape" transform="translate(19.0195,-19.0195)">
+			<title>Sheet.12</title>
+			<path d="M0 182.84 C-0 180.53 1.88 178.66 4.19 178.66 L109.15 178.66 C111.46 178.66 113.33 180.53 113.33 182.84 L113.33
+						 199.53 C113.33 201.84 111.46 203.7 109.15 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.53 L0 182.84 Z"
+					class="st1"/>
+		</g>
+		<g id="shape13-27" v:mID="13" v:groupContext="shape" transform="translate(19.0195,-19.0195)">
+			<title>Sheet.13</title>
+			<path d="M0 182.84 C-0 180.53 1.88 178.66 4.19 178.66 L109.15 178.66 C111.46 178.66 113.33 180.53 113.33 182.84 L113.33
+						 199.53 C113.33 201.84 111.46 203.7 109.15 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.53 L0 182.84 Z"
+					class="st2"/>
+		</g>
+		<g id="shape14-29" v:mID="14" v:groupContext="shape" transform="translate(19.0195,-22.5475)">
+			<title>Sheet.14</title>
+			<desc>Key7: Value = 1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="58.0575" cy="196.509" width="116.12" height="14.3829"/>
+			<path d="M116.11 189.32 L0 189.32 L0 203.7 L116.11 203.7 L116.11 189.32" class="st3"/>
+			<text x="15.59" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key7: Value = 1</text>		</g>
+		<g id="shape15-33" v:mID="15" v:groupContext="shape" transform="translate(141.656,-45.5615)">
+			<title>Sheet.15</title>
+			<path d="M0 169.01 L22.75 169.01 L22.75 157.45 L45.5 180.57 L22.75 203.7 L22.75 192.14 L0 192.14 L0 169.01 Z"
+					class="st5"/>
+		</g>
+		<g id="shape16-35" v:mID="16" v:groupContext="shape" transform="translate(193.22,-56.0464)">
+			<title>Sheet.16</title>
+			<path d="M0 182.84 C0 180.53 1.88 178.66 4.19 178.66 L96.55 178.66 C98.87 178.66 100.73 180.53 100.73 182.84 L100.73
+						 199.54 C100.73 201.84 98.87 203.7 96.55 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.54 L0 182.84 Z"
+					class="st1"/>
+		</g>
+		<g id="shape17-37" v:mID="17" v:groupContext="shape" transform="translate(193.22,-56.0464)">
+			<title>Sheet.17</title>
+			<path d="M0 182.84 C0 180.53 1.88 178.66 4.19 178.66 L96.55 178.66 C98.87 178.66 100.73 180.53 100.73 182.84 L100.73
+						 199.54 C100.73 201.84 98.87 203.7 96.55 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.54 L0 182.84 Z"
+					class="st2"/>
+		</g>
+		<g id="shape18-39" v:mID="18" v:groupContext="shape" transform="translate(228.157,-66.9545)">
+			<title>Sheet.18</title>
+			<desc>F</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="5.63538" cy="197.084" width="11.28" height="13.2327"/>
+			<path d="M11.27 190.47 L0 190.47 L0 203.7 L11.27 203.7 L11.27 190.47" class="st3"/>
+			<text x="2.27" y="200.39" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>F</text>		</g>
+		<g id="shape19-43" v:mID="19" v:groupContext="shape" transform="translate(234.88,-66.9545)">
+			<title>Sheet.19</title>
+			<desc>(key,</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.261" cy="197.084" width="34.53" height="13.2327"/>
+			<path d="M34.52 190.47 L0 190.47 L0 203.7 L34.52 203.7 L34.52 190.47" class="st3"/>
+			<text x="5.32" y="200.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(key,  </text>		</g>
+		<g id="shape20-47" v:mID="20" v:groupContext="shape" transform="translate(198.215,-53.7734)">
+			<title>Sheet.20</title>
+			<desc>hash_index =</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="41.4128" cy="197.084" width="82.83" height="13.2327"/>
+			<path d="M82.83 190.47 L0 190.47 L0 203.7 L82.83 203.7 L82.83 190.47" class="st3"/>
+			<text x="8.47" y="200.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash_index =  </text>		</g>
+		<g id="shape21-51" v:mID="21" v:groupContext="shape" transform="translate(274.858,-53.7734)">
+			<title>Sheet.21</title>
+			<desc>i)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="5.28241" cy="197.084" width="10.57" height="13.2327"/>
+			<path d="M10.56 190.47 L0 190.47 L0 203.7 L10.56 203.7 L10.56 190.47" class="st3"/>
+			<text x="2.22" y="200.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>i)</text>		</g>
+		<g id="shape22-55" v:mID="22" v:groupContext="shape" transform="translate(351.453,-93.7923)">
+			<title>Sheet.22</title>
+			<path d="M0 182.84 C0 180.53 1.88 178.66 4.19 178.66 L109.16 178.66 C111.47 178.66 113.33 180.53 113.33 182.84 L113.33
+						 199.54 C113.33 201.84 111.47 203.7 109.16 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.54 L0 182.84 Z"
+					class="st1"/>
+		</g>
+		<g id="shape23-57" v:mID="23" v:groupContext="shape" transform="translate(351.453,-93.7923)">
+			<title>Sheet.23</title>
+			<path d="M0 182.84 C0 180.53 1.88 178.66 4.19 178.66 L109.16 178.66 C111.47 178.66 113.33 180.53 113.33 182.84 L113.33
+						 199.54 C113.33 201.84 111.47 203.7 109.16 203.7 L4.19 203.7 C1.88 203.7 0 201.84 0 199.54 L0 182.84 Z"
+					class="st2"/>
+		</g>
+		<g id="shape24-59" v:mID="24" v:groupContext="shape" transform="translate(355.798,-97.3147)">
+			<title>Sheet.24</title>
+			<desc>Key1: Position 4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="51.7083" cy="196.509" width="103.42" height="14.3829"/>
+			<path d="M103.42 189.32 L0 189.32 L0 203.7 L103.42 203.7 L103.42 189.32" class="st3"/>
+			<text x="8.41" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key1: Position 4</text>		</g>
+		<g id="shape25-63" v:mID="25" v:groupContext="shape" transform="translate(351.453,-68.9879)">
+			<title>Sheet.25</title>
+			<path d="M0 182.94 C0 180.65 1.88 178.78 4.17 178.78 L109.18 178.78 C111.47 178.78 113.33 180.65 113.33 182.94 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.17 203.7 C1.88 203.7 0 201.84 0 199.55 L0 182.94 Z"
+					class="st1"/>
+		</g>
+		<g id="shape26-65" v:mID="26" v:groupContext="shape" transform="translate(351.453,-68.9879)">
+			<title>Sheet.26</title>
+			<path d="M0 182.94 C0 180.65 1.88 178.78 4.17 178.78 L109.18 178.78 C111.47 178.78 113.33 180.65 113.33 182.94 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.17 203.7 C1.88 203.7 0 201.84 0 199.55 L0 182.94 Z"
+					class="st2"/>
+		</g>
+		<g id="shape27-67" v:mID="27" v:groupContext="shape" transform="translate(355.798,-72.4921)">
+			<title>Sheet.27</title>
+			<desc>Key3: Position 6</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="51.7083" cy="196.509" width="103.42" height="14.3829"/>
+			<path d="M103.42 189.32 L0 189.32 L0 203.7 L103.42 203.7 L103.42 189.32" class="st3"/>
+			<text x="8.41" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key3: Position 6</text>		</g>
+		<g id="shape28-71" v:mID="28" v:groupContext="shape" transform="translate(351.453,-44.0636)">
+			<title>Sheet.28</title>
+			<path d="M0 182.94 C0 180.65 1.88 178.78 4.17 178.78 L109.18 178.78 C111.47 178.78 113.33 180.65 113.33 182.94 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.17 203.7 C1.88 203.7 0 201.84 0 199.55 L0 182.94 Z"
+					class="st1"/>
+		</g>
+		<g id="shape29-73" v:mID="29" v:groupContext="shape" transform="translate(351.453,-44.0636)">
+			<title>Sheet.29</title>
+			<path d="M0 182.94 C0 180.65 1.88 178.78 4.17 178.78 L109.18 178.78 C111.47 178.78 113.33 180.65 113.33 182.94 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.17 203.7 C1.88 203.7 0 201.84 0 199.55 L0 182.94 Z"
+					class="st2"/>
+		</g>
+		<g id="shape30-75" v:mID="30" v:groupContext="shape" transform="translate(351.215,-47.5198)">
+			<title>Sheet.30</title>
+			<desc>Key4: Position 14</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="55.5403" cy="196.509" width="111.09" height="14.3829"/>
+			<path d="M111.08 189.32 L0 189.32 L0 203.7 L111.08 203.7 L111.08 189.32" class="st3"/>
+			<text x="8.91" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key4: Position 14</text>		</g>
+		<g id="shape31-79" v:mID="31" v:groupContext="shape" transform="translate(351.453,-19.4988)">
+			<title>Sheet.31</title>
+			<path d="M0 182.94 C0 180.65 1.88 178.78 4.17 178.78 L109.18 178.78 C111.47 178.78 113.33 180.65 113.33 182.94 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.17 203.7 C1.88 203.7 0 201.84 0 199.55 L0 182.94 Z"
+					class="st1"/>
+		</g>
+		<g id="shape32-81" v:mID="32" v:groupContext="shape" transform="translate(351.453,-19.4988)">
+			<title>Sheet.32</title>
+			<path d="M0 182.94 C0 180.65 1.88 178.78 4.17 178.78 L109.18 178.78 C111.47 178.78 113.33 180.65 113.33 182.94 L113.33
+						 199.55 C113.33 201.84 111.47 203.7 109.18 203.7 L4.17 203.7 C1.88 203.7 0 201.84 0 199.55 L0 182.94 Z"
+					class="st2"/>
+		</g>
+		<g id="shape33-83" v:mID="33" v:groupContext="shape" transform="translate(351.215,-22.9565)">
+			<title>Sheet.33</title>
+			<desc>Key7: Position 14</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="55.5403" cy="196.509" width="111.09" height="14.3829"/>
+			<path d="M111.08 189.32 L0 189.32 L0 203.7 L111.08 203.7 L111.08 189.32" class="st3"/>
+			<text x="8.91" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key7: Position 14</text>		</g>
+		<g id="shape34-87" v:mID="34" v:groupContext="shape" transform="translate(299.89,-46.0408)">
+			<title>Sheet.34</title>
+			<path d="M0 169.01 L22.75 169.01 L22.75 157.45 L45.5 180.57 L22.75 203.7 L22.75 192.14 L0 192.14 L0 169.01 Z"
+					class="st5"/>
+		</g>
+		<g id="shape35-89" v:mID="35" v:groupContext="shape" transform="translate(528.896,-117.518)">
+			<title>Sheet.35</title>
+			<path d="M0 182.94 C0 180.66 1.89 178.78 4.17 178.78 L137.64 178.78 C139.92 178.78 141.79 180.66 141.79 182.94 L141.79
+						 199.57 C141.79 201.84 139.92 203.7 137.64 203.7 L4.17 203.7 C1.89 203.7 0 201.84 0 199.57 L0 182.94 Z"
+					class="st1"/>
+		</g>
+		<g id="shape36-91" v:mID="36" v:groupContext="shape" transform="translate(528.896,-117.518)">
+			<title>Sheet.36</title>
+			<path d="M0 182.94 C0 180.66 1.89 178.78 4.17 178.78 L137.64 178.78 C139.92 178.78 141.79 180.66 141.79 182.94 L141.79
+						 199.57 C141.79 201.84 139.92 203.7 137.64 203.7 L4.17 203.7 C1.89 203.7 0 201.84 0 199.57 L0 182.94 Z"
+					class="st2"/>
+		</g>
+		<g id="shape37-93" v:mID="37" v:groupContext="shape" transform="translate(530.056,-121.017)">
+			<title>Sheet.37</title>
+			<desc>0000</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="19.1585" cy="196.509" width="38.32" height="14.3829"/>
+			<path d="M38.32 189.32 L0 189.32 L0 203.7 L38.32 203.7 L38.32 189.32" class="st3"/>
+			<text x="5.83" y="200.1" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0000  </text>		</g>
+		<g id="shape38-97" v:mID="38" v:groupContext="shape" transform="translate(567.215,-121.017)">
+			<title>Sheet.38</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="5.7483" cy="196.509" width="11.5" height="14.3829"/>
+			<path d="M11.5 189.32 L0 189.32 L0 203.7 L11.5 203.7 L11.5 189.32" class="st3"/>
+			<text x="2.42" y="200.1" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape39-101" v:mID="39" v:groupContext="shape" transform="translate(576.215,-121.017)">
+			<title>Sheet.39</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="5.7483" cy="196.509" width="11.5" height="14.3829"/>
+			<path d="M11.5 189.32 L0 189.32 L0 203.7 L11.5 203.7 L11.5 189.32" class="st3"/>
+			<text x="2.42" y="200.1" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape40-105" v:mID="40" v:groupContext="shape" transform="translate(584.486,-121.017)">
+			<title>Sheet.40</title>
+			<desc>1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="5.7483" cy="196.509" width="11.5" height="14.3829"/>
+			<path d="M11.5 189.32 L0 189.32 L0 203.7 L11.5 203.7 L11.5 189.32" class="st3"/>
+			<text x="2.42" y="200.1" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text>		</g>
+		<g id="shape41-109" v:mID="41" v:groupContext="shape" transform="translate(588.646,-121.017)">
+			<title>Sheet.41</title>
+			<desc>0 0000 00</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="32.5687" cy="196.509" width="65.14" height="14.3829"/>
+			<path d="M65.14 189.32 L0 189.32 L0 203.7 L65.14 203.7 L65.14 189.32" class="st3"/>
+			<text x="5.91" y="200.1" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0 0000 00</text>		</g>
+		<g id="shape42-113" v:mID="42" v:groupContext="shape" transform="translate(644.965,-121.017)">
+			<title>Sheet.42</title>
+			<desc>?</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="6.12511" cy="196.509" width="12.26" height="14.3829"/>
+			<path d="M12.25 189.32 L0 189.32 L0 203.7 L12.25 203.7 L12.25 189.32" class="st3"/>
+			<text x="2.47" y="200.1" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>?</text>		</g>
+		<g id="shape43-117" v:mID="43" v:groupContext="shape" transform="translate(654.718,-121.017)">
+			<title>Sheet.43</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="5.7483" cy="196.509" width="11.5" height="14.3829"/>
+			<path d="M11.5 189.32 L0 189.32 L0 203.7 L11.5 203.7 L11.5 189.32" class="st3"/>
+			<text x="2.42" y="200.1" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape44-121" v:mID="44" v:groupContext="shape" transform="translate(464.786,-105.296)">
+			<title>Sheet.44</title>
+			<path d="M0 203.7 L108.29 203.7 C108.86 203.7 109.31 203.22 109.31 202.68 L109.31 189.5 L107.27 189.5 L107.27 202.68
+						 L108.29 201.66 L0 201.66 L0 203.7 ZM111.35 190.52 L108.29 184.41 L105.23 190.55 L111.35 190.52 Z"
+					class="st11"/>
+		</g>
+		<g id="shape45-123" v:mID="45" v:groupContext="shape" transform="translate(464.786,-80.4315)">
+			<title>Sheet.45</title>
+			<path d="M0 203.7 L123.63 203.7 C124.2 203.7 124.65 203.25 124.65 202.68 L124.65 164.28 L122.61 164.28 L122.61 202.68
+						 L123.63 201.66 L0 201.66 L0 203.7 ZM126.69 165.3 L123.6 159.18 L120.57 165.33 L126.69 165.3 Z"
+					class="st11"/>
+		</g>
+		<g id="shape46-125" v:mID="46" v:groupContext="shape" transform="translate(464.786,-55.4772)">
+			<title>Sheet.46</title>
+			<path d="M0 203.7 L186.48 203.7 C186.75 203.7 186.99 203.61 187.2 203.4 C187.38 203.22 187.5 202.95 187.5 202.68 L187.41
+						 139.32 L185.37 139.32 L185.46 202.68 L186.48 201.66 L0 201.66 L0 203.7 ZM189.51 140.07 L185.94 134.23 L183.41
+						 140.61 L189.51 140.07 Z" class="st11"/>
+		</g>
+		<g id="shape47-127" v:mID="47" v:groupContext="shape" transform="translate(464.786,-30.9125)">
+			<title>Sheet.47</title>
+			<path d="M0 203.7 L186.48 203.7 C186.75 203.7 186.99 203.61 187.2 203.4 C187.38 203.22 187.5 202.95 187.5 202.68 L187.41
+						 114.76 L185.37 114.76 L185.46 202.68 L186.48 201.66 L0 201.66 L0 203.7 ZM189.51 115.51 L185.94 109.67 L183.41
+						 116.05 L189.51 115.51 Z" class="st11"/>
+		</g>
+		<g id="shape48-129" v:mID="48" v:groupContext="shape" transform="translate(442.996,-151.106)">
+			<title>Sheet.48</title>
+			<path d="M0 179.56 C0 176.89 2.19 174.7 4.86 174.7 L70.8 174.7 C73.47 174.7 75.64 176.89 75.64 179.56 L75.64 198.88 C75.64
+						 201.54 73.47 203.7 70.8 203.7 L4.86 203.7 C2.19 203.7 0 201.54 0 198.88 L0 179.56 Z" class="st5"/>
+		</g>
+		<g id="shape49-131" v:mID="49" v:groupContext="shape" transform="translate(443.529,-155.018)">
+			<title>Sheet.49</title>
+			<desc>Values</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="37.8175" cy="192.914" width="75.64" height="21.5726"/>
+			<path d="M75.64 182.13 L0 182.13 L0 203.7 L75.64 203.7 L75.64 182.13" class="st3"/>
+			<text x="10.34" y="198.31" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Values</text>		</g>
+		<g id="shape50-135" v:mID="50" v:groupContext="shape" transform="translate(102.458,-122.192)">
+			<title>Sheet.50</title>
+			<path d="M0 203.7 C-0 199.21 0.62 195.55 1.37 195.55 L11.67 195.55 C12.42 195.55 13.03 191.9 13.03 187.4 C13.03 191.9
+						 13.64 195.55 14.39 195.55 L24.69 195.55 C25.44 195.55 26.05 199.21 26.05 203.7" class="st13"/>
+		</g>
+		<g id="shape51-138" v:mID="51" v:groupContext="shape" transform="translate(115.454,-137.5)">
+			<title>Sheet.51</title>
+			<path d="M0.2 203.7 L322.66 174.12 L322.48 172.1 L0 201.68 L0.2 203.7 L0.2 203.7 ZM321.84 176.24 L327.66 172.64 L321.28
+						 170.16 L321.84 176.24 L321.84 176.24 Z" class="st14"/>
+		</g>
+		<g id="shape52-140" v:mID="52" v:groupContext="shape" transform="translate(518.211,-142.473)">
+			<title>Sheet.52</title>
+			<path d="M0.99 176.74 L44.78 200.38 L43.82 202.17 L0 178.51 L0.99 176.74 L0.99 176.74 ZM44.87 198.1 L48.8 203.7 L41.96
+						 203.46 L44.87 198.1 L44.87 198.1 Z" class="st11"/>
+		</g>
+		<g id="shape53-142" v:mID="53" v:groupContext="shape" transform="translate(518.331,-141.963)">
+			<title>Sheet.53</title>
+			<path d="M0.75 176.17 L60.09 200.32 L59.34 202.2 L0 178.06 L0.75 176.17 L0.75 176.17 ZM59.91 198.04 L64.44 203.19 L57.6
+						 203.7 L59.91 198.04 L59.91 198.04 Z" class="st11"/>
+		</g>
+		<g id="shape54-144" v:mID="54" v:groupContext="shape" transform="translate(576.558,-153.706)">
+			<title>Sheet.54</title>
+			<path d="M0 177.04 C0 174.1 2.4 171.71 5.34 171.71 L101.51 171.71 C104.48 171.71 106.85 174.1 106.85 177.04 L106.85 198.37
+						 C106.85 201.33 104.48 203.7 101.51 203.7 L5.34 203.7 C2.4 203.7 0 201.33 0 198.37 L0 177.04 Z" class="st1"/>
+		</g>
+		<g id="shape55-146" v:mID="55" v:groupContext="shape" transform="translate(577.365,-151.611)">
+			<title>Sheet.55</title>
+			<path d="M0 177.04 C0 174.1 2.4 171.71 5.34 171.71 L101.51 171.71 C104.48 171.71 106.85 174.1 106.85 177.04 L106.85 198.37
+						 C106.85 201.33 104.48 203.7 101.51 203.7 L5.34 203.7 C2.4 203.7 0 201.33 0 198.37 L0 177.04 Z" class="st2"/>
+		</g>
+		<g id="shape56-148" v:mID="56" v:groupContext="shape" transform="translate(593.952,-167.894)">
+			<title>Sheet.56</title>
+			<desc>Lookup_table</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.2942" cy="196.509" width="86.59" height="14.3829"/>
+			<path d="M86.59 189.32 L0 189.32 L0 203.7 L86.59 203.7 L86.59 189.32" class="st3"/>
+			<text x="7.31" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Lookup_table</text>		</g>
+		<g id="shape57-152" v:mID="57" v:groupContext="shape" transform="translate(608.239,-153.515)">
+			<title>Sheet.57</title>
+			<desc>(16 bits)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="26.8054" cy="196.509" width="53.62" height="14.3829"/>
+			<path d="M53.61 189.32 L0 189.32 L0 203.7 L53.61 203.7 L53.61 189.32" class="st3"/>
+			<text x="5.16" y="200.1" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(16 bits)</text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i11.svg b/doc/guides/prog_guide/img/efd_i11.svg
new file mode 100644
index 0000000..f2cc656
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i11.svg
@@ -0,0 +1,319 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i12.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="10.2783in" height="4.28958in"
+		viewBox="0 0 740.039 308.85" xml:space="preserve" color-interpolation-filters="sRGB" class="st21">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {fill:#ffffff;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st2 {fill:none;stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st3 {stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st4 {fill:#000000;font-family:Arial;font-size:0.998566em}
+		.st5 {fill:#0071c5;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st6 {stroke:#00b050;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st7 {stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st8 {fill:#000000;font-family:Arial;font-size:0.918686em;font-weight:bold}
+		.st9 {fill:#00b050;font-size:1em}
+		.st10 {fill:#c00000;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st11 {fill:#004280;stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.00749637}
+		.st12 {fill:#ffffff;font-family:Arial;font-size:1.16833em}
+		.st13 {fill:#2e75b5;stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st14 {fill:#ffffff;font-family:Arial;font-size:1.16666em}
+		.st15 {font-size:1em}
+		.st16 {fill:none;stroke:none;stroke-width:0.25}
+		.st17 {fill:#000000;font-family:Calibri;font-size:1.00001em}
+		.st18 {marker-end:url(#mrkr5-121);stroke:#5b9bd5;stroke-dasharray:1.5,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st19 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
+		.st20 {fill:#000000;font-family:Calibri;font-size:1.16666em}
+		.st21 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-121" class="st19" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+		</marker>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape5-1" v:mID="5" v:groupContext="shape" transform="translate(36.0674,-256.878)">
+			<title>Sheet.5</title>
+			<path d="M0 291.88 C0 290 1.52 288.48 3.41 288.48 L68.51 288.48 C70.4 288.48 71.91 290 71.91 291.88 L71.91 305.46 C71.91
+						 307.33 70.4 308.85 68.51 308.85 L3.41 308.85 C1.52 308.85 0 307.33 0 305.46 L0 291.88 Z" class="st1"/>
+		</g>
+		<g id="shape6-3" v:mID="6" v:groupContext="shape" transform="translate(36.0674,-256.878)">
+			<title>Sheet.6</title>
+			<path d="M0 291.88 C0 290 1.52 288.48 3.41 288.48 L68.51 288.48 C70.4 288.48 71.91 290 71.91 291.88 L71.91 305.46 C71.91
+						 307.33 70.4 308.85 68.51 308.85 L3.41 308.85 C1.52 308.85 0 307.33 0 305.46 L0 291.88 Z" class="st2"/>
+		</g>
+		<g id="shape7-5" v:mID="7" v:groupContext="shape" transform="translate(61.6502,-258.089)">
+			<title>Sheet.7</title>
+			<desc>Key</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="13.7891" cy="301.658" width="27.58" height="14.3829"/>
+			<path d="M27.58 294.47 L0 294.47 L0 308.85 L27.58 308.85 L27.58 294.47" class="st3"/>
+			<text x="3.46" y="305.25" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key</text>		</g>
+		<g id="shape8-9" v:mID="8" v:groupContext="shape" transform="translate(51.9748,-236.328)">
+			<title>Sheet.8</title>
+			<path d="M0 298.54 L9.81 298.54 L9.81 288.24 L29.44 288.24 L29.44 298.54 L39.26 298.54 L19.63 308.85 L0 298.54 Z"
+					class="st5"/>
+		</g>
+		<g id="shape9-11" v:mID="9" v:groupContext="shape" transform="translate(36.0674,-215.298)">
+			<title>Sheet.9</title>
+			<path d="M0 291.77 C0 289.89 1.54 288.36 3.42 288.36 L68.49 288.36 C70.38 288.36 71.91 289.89 71.91 291.77 L71.91 305.43
+						 C71.91 307.32 70.38 308.85 68.49 308.85 L3.42 308.85 C1.54 308.85 0 307.32 0 305.43 L0 291.77 Z"
+					class="st1"/>
+		</g>
+		<g id="shape10-13" v:mID="10" v:groupContext="shape" transform="translate(36.0674,-215.298)">
+			<title>Sheet.10</title>
+			<path d="M0 291.77 C0 289.89 1.54 288.36 3.42 288.36 L68.49 288.36 C70.38 288.36 71.91 289.89 71.91 291.77 L71.91 305.43
+						 C71.91 307.32 70.38 308.85 68.49 308.85 L3.42 308.85 C1.54 308.85 0 307.32 0 305.43 L0 291.77 Z"
+					class="st2"/>
+		</g>
+		<g id="shape11-15" v:mID="11" v:groupContext="shape" transform="translate(58.8889,-216.57)">
+			<title>Sheet.11</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.8573" cy="301.658" width="33.72" height="14.3829"/>
+			<path d="M33.71 294.47 L0 294.47 L0 308.85 L33.71 308.85 L33.71 294.47" class="st3"/>
+			<text x="3.86" y="305.25" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape12-19" v:mID="12" v:groupContext="shape" transform="translate(27.3033,-174.437)">
+			<title>Sheet.12</title>
+			<path d="M0 292.58 C0 290.78 1.46 289.32 3.26 289.32 L87.15 289.32 C88.95 289.32 90.4 290.78 90.4 292.58 L90.4 305.6
+						 C90.4 307.4 88.95 308.85 87.15 308.85 L3.26 308.85 C1.46 308.85 0 307.4 0 305.6 L0 292.58 Z" class="st1"/>
+		</g>
+		<g id="shape13-21" v:mID="13" v:groupContext="shape" transform="translate(27.3033,-174.437)">
+			<title>Sheet.13</title>
+			<path d="M0 292.58 C0 290.78 1.46 289.32 3.26 289.32 L87.15 289.32 C88.95 289.32 90.4 290.78 90.4 292.58 L90.4 305.6
+						 C90.4 307.4 88.95 308.85 87.15 308.85 L3.26 308.85 C1.46 308.85 0 307.4 0 305.6 L0 292.58 Z" class="st2"/>
+		</g>
+		<g id="shape14-23" v:mID="14" v:groupContext="shape" transform="translate(36.0515,-175.256)">
+			<title>Sheet.14</title>
+			<desc>0x0102ABCD</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.6644" cy="301.658" width="87.33" height="14.3829"/>
+			<path d="M87.33 294.47 L0 294.47 L0 308.85 L87.33 308.85 L87.33 294.47" class="st3"/>
+			<text x="7.36" y="305.25" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0102ABCD</text>		</g>
+		<g id="shape15-27" v:mID="15" v:groupContext="shape" transform="translate(51.9748,-194.029)">
+			<title>Sheet.15</title>
+			<path d="M0 298.48 L9.81 298.48 L9.81 288.12 L29.44 288.12 L29.44 298.48 L39.26 298.48 L19.63 308.85 L0 298.48 Z"
+					class="st5"/>
+		</g>
+		<g id="shape16-29" v:mID="16" v:groupContext="shape" transform="translate(48.9133,-159.818)">
+			<title>Sheet.16</title>
+			<path d="M26.41 296.87 C26.41 300.18 25.97 302.86 25.41 302.86 L14.21 302.86 C13.66 302.86 13.21 305.55 13.21 308.85
+						 C13.21 305.55 12.76 302.86 12.21 302.86 L1.01 302.86 C0.45 302.86 0 300.18 0 296.87" class="st6"/>
+		</g>
+		<g id="shape17-32" v:mID="17" v:groupContext="shape" transform="translate(19.0195,-19.0195)">
+			<title>Sheet.17</title>
+			<path d="M0 196.93 L0 308.85 L145.15 308.85 L145.15 196.93 L0 196.93 L0 196.93 Z" class="st1"/>
+		</g>
+		<g id="shape18-34" v:mID="18" v:groupContext="shape" transform="translate(19.0195,-19.0195)">
+			<title>Sheet.18</title>
+			<path d="M0 196.93 L145.15 196.93 L145.15 308.85 L0 308.85 L0 196.93" class="st7"/>
+		</g>
+		<g id="shape19-37" v:mID="19" v:groupContext="shape" transform="translate(28.2638,-70.6655)">
+			<title>Sheet.19</title>
+			<path d="M0 280.69 C0 277.58 2.53 275.06 5.64 275.06 L124.14 275.06 C127.26 275.06 129.78 277.58 129.78 280.69 L129.78
+						 303.22 C129.78 306.33 127.26 308.85 124.14 308.85 L5.64 308.85 C2.53 308.85 0 306.33 0 303.22 L0 280.69
+						 Z" class="st1"/>
+		</g>
+		<g id="shape20-39" v:mID="20" v:groupContext="shape" transform="translate(28.2638,-70.6655)">
+			<title>Sheet.20</title>
+			<path d="M0 280.69 C0 277.58 2.53 275.06 5.64 275.06 L124.14 275.06 C127.26 275.06 129.78 277.58 129.78 280.69 L129.78
+						 303.22 C129.78 306.33 127.26 308.85 124.14 308.85 L5.64 308.85 C2.53 308.85 0 306.33 0 303.22 L0 280.69
+						 Z" class="st2"/>
+		</g>
+		<g id="shape21-41" v:mID="21" v:groupContext="shape" transform="translate(57.4514,-85.7513)">
+			<title>Sheet.21</title>
+			<desc>hash_index =</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="45.0133" cy="301.658" width="90.03" height="14.3829"/>
+			<path d="M90.03 294.47 L0 294.47 L0 308.85 L90.03 308.85 L90.03 294.47" class="st3"/>
+			<text x="9.2" y="305.25" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash_index =  </text>		</g>
+		<g id="shape22-45" v:mID="22" v:groupContext="shape" transform="translate(76.3001,-71.3719)">
+			<title>Sheet.22</title>
+			<desc>38123</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="21.0762" cy="301.658" width="42.16" height="14.3829"/>
+			<path d="M42.15 294.47 L0 294.47 L0 308.85 L42.15 308.85 L42.15 294.47" class="st3"/>
+			<text x="4.42" y="305.25" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>38123</text>		</g>
+		<g id="shape23-49" v:mID="23" v:groupContext="shape" transform="translate(28.2638,-27.048)">
+			<title>Sheet.23</title>
+			<path d="M0 280.69 C0 277.59 2.54 275.06 5.64 275.06 L124.14 275.06 C127.26 275.06 129.78 277.59 129.78 280.69 L129.78
+						 303.22 C129.78 306.33 127.26 308.85 124.14 308.85 L5.64 308.85 C2.54 308.85 0 306.33 0 303.22 L0 280.69
+						 Z" class="st1"/>
+		</g>
+		<g id="shape24-51" v:mID="24" v:groupContext="shape" transform="translate(28.2638,-27.048)">
+			<title>Sheet.24</title>
+			<path d="M0 280.69 C0 277.59 2.54 275.06 5.64 275.06 L124.14 275.06 C127.26 275.06 129.78 277.59 129.78 280.69 L129.78
+						 303.22 C129.78 306.33 127.26 308.85 124.14 308.85 L5.64 308.85 C2.54 308.85 0 306.33 0 303.22 L0 280.69
+						 Z" class="st2"/>
+		</g>
+		<g id="shape25-53" v:mID="25" v:groupContext="shape" transform="translate(54.0924,-41.564)">
+			<title>Sheet.25</title>
+			<desc>lookup_table =</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="46.931" cy="301.658" width="93.87" height="14.3829"/>
+			<path d="M93.86 294.47 L0 294.47 L0 308.85 L93.86 308.85 L93.86 294.47" class="st3"/>
+			<text x="7.79" y="305.25" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>lookup_table =</text>		</g>
+		<g id="shape26-57" v:mID="26" v:groupContext="shape" transform="translate(28.0195,-28.5506)">
+			<title>Sheet.26</title>
+			<desc>0110 1100 0101 1101</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="64.89" cy="302.233" width="129.79" height="13.2327"/>
+			<path d="M129.78 295.62 L0 295.62 L0 308.85 L129.78 308.85 L129.78 295.62" class="st3"/>
+			<text x="11.25" y="305.54" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0110 11<tspan
+						class="st9">0</tspan>0 0101 1101</text>		</g>
+		<g id="shape27-62" v:mID="27" v:groupContext="shape" transform="translate(26.2461,-113.863)">
+			<title>Sheet.27</title>
+			<desc>Group ID: 0x0102</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="48.6286" cy="302.881" width="97.26" height="11.9384"/>
+			<path d="M97.26 296.91 L0 296.91 L0 308.85 L97.26 308.85 L97.26 296.91" class="st3"/>
+			<text x="7.73" y="305.86" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group ID: 0x0102</text>		</g>
+		<g id="shape28-66" v:mID="28" v:groupContext="shape" transform="translate(42.3703,-135.313)">
+			<title>Sheet.28</title>
+			<path d="M0 298.48 L9.84 298.48 L9.84 288.12 L29.53 288.12 L29.53 298.48 L39.38 298.48 L19.69 308.85 L0 298.48 Z"
+					class="st5"/>
+		</g>
+		<g id="shape29-68" v:mID="29" v:groupContext="shape" transform="translate(117.645,-244.476)">
+			<title>Sheet.29</title>
+			<path d="M0 274.07 L22.75 274.07 L22.75 262.48 L45.5 285.66 L22.75 308.85 L22.75 297.26 L0 297.26 L0 274.07 Z"
+					class="st5"/>
+		</g>
+		<g id="shape30-70" v:mID="30" v:groupContext="shape" transform="translate(169.209,-251.966)">
+			<title>Sheet.30</title>
+			<path d="M0 283.69 C0 280.91 2.27 278.65 5.04 278.65 L111.77 278.65 C114.56 278.65 116.81 280.91 116.81 283.69 L116.81
+						 303.82 C116.81 306.6 114.56 308.85 111.77 308.85 L5.04 308.85 C2.27 308.85 0 306.6 0 303.82 L0 283.69 Z"
+					class="st1"/>
+		</g>
+		<g id="shape31-72" v:mID="31" v:groupContext="shape" transform="translate(169.209,-251.966)">
+			<title>Sheet.31</title>
+			<path d="M0 283.69 C0 280.91 2.27 278.65 5.04 278.65 L111.77 278.65 C114.56 278.65 116.81 280.91 116.81 283.69 L116.81
+						 303.82 C116.81 306.6 114.56 308.85 111.77 308.85 L5.04 308.85 C2.27 308.85 0 306.6 0 303.82 L0 283.69 Z"
+					class="st2"/>
+		</g>
+		<g id="shape35-74" v:mID="35" v:groupContext="shape" transform="translate(291.966,-244.476)">
+			<title>Sheet.35</title>
+			<path d="M0 274.07 L22.69 274.07 L22.69 262.48 L45.38 285.66 L22.69 308.85 L22.69 297.26 L0 297.26 L0 274.07 Z"
+					class="st5"/>
+		</g>
+		<g id="shape36-76" v:mID="36" v:groupContext="shape" transform="translate(343.17,-254.482)">
+			<title>Sheet.36</title>
+			<path d="M0 288.09 C0 285.8 1.88 283.93 4.17 283.93 L109.18 283.93 C111.47 283.93 113.33 285.8 113.33 288.09 L113.33
+						 304.7 C113.33 306.99 111.47 308.85 109.18 308.85 L4.17 308.85 C1.88 308.85 0 306.99 0 304.7 L0 288.09 Z"
+					class="st1"/>
+		</g>
+		<g id="shape37-78" v:mID="37" v:groupContext="shape" transform="translate(343.17,-254.482)">
+			<title>Sheet.37</title>
+			<path d="M0 288.09 C0 285.8 1.88 283.93 4.17 283.93 L109.18 283.93 C111.47 283.93 113.33 285.8 113.33 288.09 L113.33
+						 304.7 C113.33 306.99 111.47 308.85 109.18 308.85 L4.17 308.85 C1.88 308.85 0 306.99 0 304.7 L0 288.09 Z"
+					class="st2"/>
+		</g>
+		<g id="shape38-80" v:mID="38" v:groupContext="shape" transform="translate(368.337,-257.958)">
+			<title>Sheet.38</title>
+			<desc>Position = 6</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="38.1131" cy="301.658" width="76.23" height="14.3829"/>
+			<path d="M76.23 294.47 L0 294.47 L0 308.85 L76.23 308.85 L76.23 294.47" class="st3"/>
+			<text x="6.64" y="305.25" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Position = 6</text>		</g>
+		<g id="shape39-84" v:mID="39" v:groupContext="shape" transform="translate(158.044,-86.5202)">
+			<title>Sheet.39</title>
+			<path d="M0 308.85 L69.59 308.85 C70.16 308.85 70.62 308.39 70.62 307.83 L70.62 148.5 L68.57 148.5 L68.57 307.83 L69.59
+						 306.81 L0 306.81 L0 308.85 ZM72.66 149.52 L69.59 143.4 L66.53 149.52 L72.66 149.52 Z" class="st11"/>
+		</g>
+		<g id="shape41-86" v:mID="41" v:groupContext="shape" transform="translate(335.112,-199.647)">
+			<title>Sheet.41</title>
+			<desc>Apply the equation</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="71.2648" cy="300.436" width="142.53" height="16.8275"/>
+			<path d="M142.53 292.02 L0 292.02 L0 308.85 L142.53 308.85 L142.53 292.02" class="st3"/>
+			<text x="13.19" y="304.64" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Apply the equation  </text>		</g>
+		<g id="shape42-90" v:mID="42" v:groupContext="shape" transform="translate(341.115,-182.871)">
+			<title>Sheet.42</title>
+			<desc>to retrieve the bit</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="64.5256" cy="300.436" width="129.06" height="16.8275"/>
+			<path d="M129.05 292.02 L0 292.02 L0 308.85 L129.05 308.85 L129.05 292.02" class="st3"/>
+			<text x="12.31" y="304.64" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>to retrieve the bit  </text>		</g>
+		<g id="shape43-94" v:mID="43" v:groupContext="shape" transform="translate(349.999,-166.095)">
+			<title>Sheet.43</title>
+			<desc>position in the</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="54.2285" cy="300.436" width="108.46" height="16.8275"/>
+			<path d="M108.46 292.02 L0 292.02 L0 308.85 L108.46 308.85 L108.46 292.02" class="st3"/>
+			<text x="10.97" y="304.64" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>position in the  </text>		</g>
+		<g id="shape44-98" v:mID="44" v:groupContext="shape" transform="translate(353.361,-149.319)">
+			<title>Sheet.44</title>
+			<desc>lookup_table</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="47.9619" cy="300.436" width="95.93" height="16.8275"/>
+			<path d="M95.92 292.02 L0 292.02 L0 308.85 L95.92 308.85 L95.92 292.02" class="st3"/>
+			<text x="8.21" y="304.64" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>lookup_table</text>		</g>
+		<g id="shape47-102" v:mID="47" v:groupContext="shape" transform="translate(115.17,255.2) rotate(-90)">
+			<title>1-D word balloon</title>
+			<desc>Retrieve the value “0&#39; from the specified location in the loo...</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+				<v:ud v:nameU="Scale" v:val="VT0(1):26"/>
+				<v:ud v:nameU="AntiScale" v:val="VT0(1):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="29.2016" cy="218.85" width="180" height="58.4032" transform="rotate(90)"/>
+			<path d="M0 308.85 L58.4 308.85 L58.4 128.85 L0 128.85 L0 204.67 L-11.87 38.85 L-7.09 233.03 L0 233.03 L0 308.85 Z"
+					class="st13"/>
+			<text x="136.98" y="-41.8" transform="rotate(90)" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Retrieve the value “0&#39; from <tspan
+						x="134.41" dy="1.2em" class="st15">the specified location in the </tspan><tspan x="181.1" dy="1.2em"
+						class="st15">lookup table</tspan></text>		</g>
+		<g id="shape48-107" v:mID="48" v:groupContext="shape" transform="translate(169.209,-251.966)">
+			<title>Sheet.48</title>
+			<desc>F(Key, hash_index = 38123</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54.2285" cy="295.35" width="108.46" height="27"/>
+			<rect x="0" y="281.85" width="108.457" height="27" class="st16"/>
+			<text x="5.86" y="291.75" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>F(Key, hash_index = <tspan
+						x="39.02" dy="1.2em" class="st15">38123</tspan></text>		</g>
+		<g id="shape49-111" v:mID="49" v:groupContext="shape" transform="translate(553.962,99) rotate(90)">
+			<title>1-D word balloon.49</title>
+			<desc>Apply the equation to retrieve the bit position in the lookup...</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+				<v:ud v:nameU="Scale" v:val="VT0(1):26"/>
+				<v:ud v:nameU="AntiScale" v:val="VT0(1):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="29.2016" cy="218.85" width="180" height="58.4032" transform="rotate(-90)"/>
+			<path d="M0 308.85 L58.4 308.85 L58.4 128.85 L0 128.85 L0 204.67 L-51.13 299.85 L0 233.03 L0 308.85 Z" class="st13"/>
+			<text x="-284.62" y="16.6" transform="rotate(-90)" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Apply the equation to <tspan
+						x="-296.67" dy="1.2em" class="st15">retrieve the bit position in </tspan><tspan x="-270.22" dy="1.2em"
+						class="st15">the lookup</tspan>_table</text>		</g>
+		<g id="shape50-116" v:mID="50" v:groupContext="shape" transform="translate(640.132,-104.709) rotate(44.1224)">
+			<title>Sheet.50</title>
+			<path d="M0 308.85 L54.13 308.85" class="st18"/>
+		</g>
+		<g id="shape51-122" v:mID="51" v:groupContext="shape" transform="translate(433.02,-122.267)">
+			<title>Sheet.51</title>
+			<desc>(Hash(key,seed1)+38123*hash(key,seed2))%16</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="144" cy="295.35" width="288" height="27"/>
+			<rect x="0" y="281.85" width="288" height="27" class="st2"/>
+			<text x="9.86" y="299.55" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(Hash(key,seed1)+38123*hash(key,seed2))%16</text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i12.svg b/doc/guides/prog_guide/img/efd_i12.svg
new file mode 100644
index 0000000..a309d58
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i12.svg
@@ -0,0 +1,1008 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i13.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="10.2932in" height="5.27505in"
+		viewBox="0 0 741.108 379.804" xml:space="preserve" color-interpolation-filters="sRGB" class="st30">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st2 {stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st3 {fill:#004280;font-family:Arial;font-size:0.828804em}
+		.st4 {fill:#ffffff;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st5 {stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st6 {fill:#7030a0;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st7 {fill:#d0d6d9;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st8 {fill:#006fc5;stroke:#006fc5;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.00749637}
+		.st9 {fill:#006fc5;stroke:#006fc5;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0149927}
+		.st10 {fill:#d0d6d9;stroke:#d0d6d9;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0299855}
+		.st11 {fill:#d0d6d9;stroke:#d0d6d9;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0149927}
+		.st12 {fill:#004280;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st13 {fill:#00b050;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st14 {fill:#ff0000;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st15 {fill:#00b050;stroke:#00b050;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0299855}
+		.st16 {fill:#c00000;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st17 {fill:#000000;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st18 {fill:#7f6d00;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st19 {fill:#ff0000;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st20 {fill:#7e8d96;font-family:Arial;font-size:0.828804em;font-weight:bold}
+		.st21 {fill:none;stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st22 {fill:#000000;font-family:Arial;font-size:0.998566em}
+		.st23 {fill:#0071c5;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st24 {stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st25 {fill:#ffffff;font-family:Arial;font-size:0.998566em}
+		.st26 {fill:#ff6600;stroke:#ff6600;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.00749637}
+		.st27 {fill:#004280;stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0299855}
+		.st28 {fill:#ff0000;stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0299855}
+		.st29 {fill:#ffffff;font-family:Arial;font-size:1.49785em}
+		.st30 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(304.703,-329.32)">
+			<title>Sheet.3</title>
+			<path d="M0 379.8 C-0 375.37 0.6 371.78 1.35 371.78 L205.05 371.78 C205.78 371.78 206.38 368.18 206.38 363.75 C206.38
+						 368.18 206.98 371.78 207.73 371.78 L411.43 371.78 C412.15 371.78 412.75 375.37 412.75 379.8" class="st1"/>
+		</g>
+		<g id="shape4-4" v:mID="4" v:groupContext="shape" transform="translate(219.943,-329.32)">
+			<title>Sheet.4</title>
+			<path d="M0 379.8 C0 375.64 0.57 372.25 1.26 372.25 L29.77 372.25 C30.48 372.25 31.03 368.88 31.03 364.71 C31.03 368.88
+						 31.6 372.25 32.29 372.25 L60.81 372.25 C61.51 372.25 62.07 375.64 62.07 379.8" class="st1"/>
+		</g>
+		<g id="shape5-7" v:mID="5" v:groupContext="shape" transform="translate(241.175,-343.9)">
+			<title>Sheet.5</title>
+			<desc>Bins</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="12.7158" cy="373.835" width="25.44" height="11.9384"/>
+			<path d="M25.43 367.87 L0 367.87 L0 379.8 L25.43 379.8 L25.43 367.87" class="st2"/>
+			<text x="3.04" y="376.82" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Bins</text>		</g>
+		<g id="shape6-11" v:mID="6" v:groupContext="shape" transform="translate(496.212,-344.504)">
+			<title>Sheet.6</title>
+			<desc>Groups</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="20.3447" cy="373.835" width="40.69" height="11.9384"/>
+			<path d="M40.69 367.87 L0 367.87 L0 379.8 L40.69 379.8 L40.69 367.87" class="st2"/>
+			<text x="4.04" y="376.82" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Groups</text>		</g>
+		<g id="shape7-15" v:mID="7" v:groupContext="shape" transform="translate(131.823,-260.299)">
+			<title>Sheet.7</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape8-17" v:mID="8" v:groupContext="shape" transform="translate(131.823,-260.299)">
+			<title>Sheet.8</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape9-20" v:mID="9" v:groupContext="shape" transform="translate(134.706,-310.738)">
+			<title>Sheet.9</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape10-24" v:mID="10" v:groupContext="shape" transform="translate(122.218,-329.32)">
+			<title>Sheet.10</title>
+			<path d="M0 379.8 C-0 375.64 0.57 372.25 1.26 372.25 L29.77 372.25 C30.47 372.25 31.03 368.88 31.03 364.71 C31.03 368.88
+						 31.6 372.25 32.29 372.25 L60.81 372.25 C61.51 372.25 62.07 375.64 62.07 379.8" class="st1"/>
+		</g>
+		<g id="shape11-27" v:mID="11" v:groupContext="shape" transform="translate(137.598,-343.9)">
+			<title>Sheet.11</title>
+			<desc>Chunks</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="20.9813" cy="373.835" width="41.97" height="11.9384"/>
+			<path d="M41.96 367.87 L0 367.87 L0 379.8 L41.96 379.8 L41.96 367.87" class="st2"/>
+			<text x="4.12" y="376.82" class="st3" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Chunks</text>		</g>
+		<g id="shape12-31" v:mID="12" v:groupContext="shape" transform="translate(131.823,-195.232)">
+			<title>Sheet.12</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape13-33" v:mID="13" v:groupContext="shape" transform="translate(131.823,-195.232)">
+			<title>Sheet.13</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape14-36" v:mID="14" v:groupContext="shape" transform="translate(134.706,-245.682)">
+			<title>Sheet.14</title>
+			<desc>1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text>		</g>
+		<g id="shape15-40" v:mID="15" v:groupContext="shape" transform="translate(131.823,-130.525)">
+			<title>Sheet.15</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st7"/>
+		</g>
+		<g id="shape16-42" v:mID="16" v:groupContext="shape" transform="translate(131.823,-130.525)">
+			<title>Sheet.16</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape17-45" v:mID="17" v:groupContext="shape" transform="translate(134.706,-180.952)">
+			<title>Sheet.17</title>
+			<desc>…</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.30974" cy="373.835" width="14.62" height="11.9384"/>
+			<path d="M14.62 367.87 L0 367.87 L0 379.8 L14.62 379.8 L14.62 367.87" class="st2"/>
+			<text x="2.34" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>…</text>		</g>
+		<g id="shape18-49" v:mID="18" v:groupContext="shape" transform="translate(131.823,-65.4584)">
+			<title>Sheet.18</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape19-51" v:mID="19" v:groupContext="shape" transform="translate(131.823,-65.4584)">
+			<title>Sheet.19</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape20-54" v:mID="20" v:groupContext="shape" transform="translate(130.403,-115.896)">
+			<title>Sheet.20</title>
+			<desc>variable</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="24.7986" cy="373.835" width="49.6" height="11.9384"/>
+			<path d="M49.6 367.87 L0 367.87 L0 379.8 L49.6 379.8 L49.6 367.87" class="st2"/>
+			<text x="6" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>variable  </text>		</g>
+		<g id="shape21-58" v:mID="21" v:groupContext="shape" transform="translate(130.403,-103.913)">
+			<title>Sheet.21</title>
+			<desc># of</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="13.347" cy="373.835" width="26.7" height="11.9384"/>
+			<path d="M26.69 367.87 L0 367.87 L0 379.8 L26.69 379.8 L26.69 367.87" class="st2"/>
+			<text x="4.51" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/># of  </text>		</g>
+		<g id="shape22-62" v:mID="22" v:groupContext="shape" transform="translate(130.403,-91.93)">
+			<title>Sheet.22</title>
+			<desc>chunks</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="21.6122" cy="373.835" width="43.23" height="11.9384"/>
+			<path d="M43.22 367.87 L0 367.87 L0 379.8 L43.22 379.8 L43.22 367.87" class="st2"/>
+			<text x="4.2" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>chunks</text>		</g>
+		<g id="shape23-66" v:mID="23" v:groupContext="shape" transform="translate(130.403,-79.9472)">
+			<title>Sheet.23</title>
+			<desc>(power</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="21.9251" cy="373.835" width="43.86" height="11.9384"/>
+			<path d="M43.85 367.87 L0 367.87 L0 379.8 L43.85 379.8 L43.85 367.87" class="st2"/>
+			<text x="5.62" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(power  </text>		</g>
+		<g id="shape24-70" v:mID="24" v:groupContext="shape" transform="translate(130.403,-67.9643)">
+			<title>Sheet.24</title>
+			<desc>of 2)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="13.6626" cy="373.835" width="27.33" height="11.9384"/>
+			<path d="M27.33 367.87 L0 367.87 L0 379.8 L27.33 379.8 L27.33 367.87" class="st2"/>
+			<text x="3.17" y="376.82" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>of 2)</text>		</g>
+		<g id="shape25-74" v:mID="25" v:groupContext="shape" transform="translate(172.289,-260.838)">
+			<title>Sheet.25</title>
+			<path d="M1.43 379.8 L3.29 375.51 L1.85 374.9 L0 379.19 L1.43 379.8 L1.43 379.8 ZM3.9 374.08 L5.76 369.79 L4.32 369.18
+						 L2.47 373.47 L3.9 374.08 L3.9 374.08 ZM6.37 368.36 L8.22 364.07 L6.79 363.45 L4.94 367.75 L6.37 368.36 L6.37
+						 368.36 ZM8.84 362.64 L10.69 358.35 L9.26 357.73 L7.41 362.02 L8.84 362.64 L8.84 362.64 ZM11.31 356.92 L13.16
+						 352.62 L11.73 352 L9.87 356.3 L11.31 356.92 L11.31 356.92 ZM13.78 351.19 L15.63 346.9 L14.2 346.28 L12.34
+						 350.57 L13.78 351.19 L13.78 351.19 ZM16.25 345.47 L18.1 341.17 L16.67 340.56 L14.81 344.85 L16.25 345.47
+						 L16.25 345.47 ZM18.71 339.74 L20.57 335.45 L19.13 334.84 L17.28 339.13 L18.71 339.74 L18.71 339.74 ZM21.18
+						 334.02 L23.04 329.73 L21.6 329.12 L19.75 333.41 L21.18 334.02 L21.18 334.02 ZM23.65 328.3 L25.5 324.01 L24.07
+						 323.39 L22.22 327.68 L23.65 328.3 L23.65 328.3 ZM26.12 322.58 L27.97 318.28 L26.54 317.67 L24.69 321.96
+						 L26.12 322.58 L26.12 322.58 ZM28.59 316.85 L29.44 314.87 L28.01 314.25 L27.16 316.24 L28.59 316.85 L28.59
+						 316.85 Z" class="st8"/>
+		</g>
+		<g id="shape26-76" v:mID="26" v:groupContext="shape" transform="translate(172.476,-20.463)">
+			<title>Sheet.26</title>
+			<path d="M1.55 203.84 L2.28 208.45 L0.74 208.7 L0 204.09 L1.55 203.84 L1.55 203.84 ZM2.52 209.99 L3.27 214.61 L1.73 214.86
+						 L0.99 210.23 L2.52 209.99 L2.52 209.99 ZM3.51 216.15 L4.25 220.76 L2.7 221 L1.97 216.39 L3.51 216.15 L3.51
+						 216.15 ZM4.49 222.3 L5.24 226.92 L3.69 227.16 L2.96 222.54 L4.49 222.3 L4.49 222.3 ZM5.48 228.45 L6.21 233.07
+						 L4.67 233.31 L3.93 228.7 L5.48 228.45 L5.48 228.45 ZM6.47 234.6 L7.2 239.22 L5.66 239.47 L4.92 234.86 L6.47
+						 234.6 L6.47 234.6 ZM7.44 240.76 L8.18 245.37 L6.65 245.63 L5.9 241 L7.44 240.76 L7.44 240.76 ZM8.43 246.91
+						 L9.17 251.53 L7.62 251.77 L6.89 247.15 L8.43 246.91 L8.43 246.91 ZM9.41 253.07 L10.14 257.68 L8.61 257.92
+						 L7.88 253.31 L9.41 253.07 L9.41 253.07 ZM10.4 259.21 L11.14 263.84 L9.59 264.08 L8.85 259.47 L10.4 259.21
+						 L10.4 259.21 ZM11.38 265.37 L12.13 269.98 L10.58 270.24 L9.84 265.62 L11.38 265.37 L11.38 265.37 ZM12.37
+						 271.52 L13.1 276.14 L11.56 276.39 L10.82 271.76 L12.37 271.52 L12.37 271.52 ZM13.34 277.68 L14.09 282.29
+						 L12.55 282.53 L11.81 277.92 L13.34 277.68 L13.34 277.68 ZM14.33 283.84 L15.07 288.45 L13.52 288.69 L12.79
+						 284.08 L14.33 283.84 L14.33 283.84 ZM15.32 289.99 L16.06 294.61 L14.51 294.85 L13.78 290.23 L15.32 289.99
+						 L15.32 289.99 ZM16.3 296.13 L17.03 300.75 L15.5 301 L14.75 296.39 L16.3 296.13 L16.3 296.13 ZM17.29 302.29
+						 L18.02 306.9 L16.48 307.16 L15.74 302.53 L17.29 302.29 L17.29 302.29 ZM18.26 308.45 L19 313.06 L17.47 313.3
+						 L16.73 308.69 L18.26 308.45 L18.26 308.45 ZM19.25 314.6 L19.99 319.22 L18.44 319.46 L17.71 314.84 L19.25
+						 314.6 L19.25 314.6 ZM20.23 320.76 L20.96 325.37 L19.43 325.61 L18.7 321 L20.23 320.76 L20.23 320.76 ZM21.22
+						 326.9 L21.96 331.51 L20.41 331.77 L19.67 327.15 L21.22 326.9 L21.22 326.9 ZM22.2 333.06 L22.95 337.67 L21.4
+						 337.92 L20.66 333.31 L22.2 333.06 L22.2 333.06 ZM23.19 339.21 L23.92 343.83 L22.38 344.07 L21.64 339.45
+						 L23.19 339.21 L23.19 339.21 ZM24.18 345.37 L24.91 349.98 L23.37 350.22 L22.63 345.61 L24.18 345.37 L24.18
+						 345.37 ZM25.15 351.52 L25.89 356.14 L24.36 356.38 L23.61 351.76 L25.15 351.52 L25.15 351.52 ZM26.14 357.67
+						 L26.88 362.28 L25.33 362.53 L24.6 357.92 L26.14 357.67 L26.14 357.67 ZM27.12 363.82 L27.85 368.44 L26.32
+						 368.69 L25.59 364.08 L27.12 363.82 L27.12 363.82 ZM28.11 369.98 L28.84 374.59 L27.3 374.83 L26.56 370.22
+						 L28.11 369.98 L28.11 369.98 ZM29.08 376.13 L29.64 379.55 L28.09 379.8 L27.55 376.37 L29.08 376.13 L29.08
+						 376.13 Z" class="st9"/>
+		</g>
+		<g id="shape27-78" v:mID="27" v:groupContext="shape" transform="translate(276.159,-233.368)">
+			<title>Sheet.27</title>
+			<path d="M0.45 294.85 L354.04 376.06 L353.59 378.04 L0 296.85 L0.45 294.85 L0.45 294.85 ZM353.5 373.84 L358.79 378.19
+						 L352.12 379.8 L353.5 373.84 L353.5 373.84 Z" class="st10"/>
+		</g>
+		<g id="shape28-80" v:mID="28" v:groupContext="shape" transform="translate(275.859,-178.726)">
+			<title>Sheet.28</title>
+			<path d="M1.05 240.32 L231.44 376.33 L230.39 378.1 L0 242.09 L1.05 240.32 L1.05 240.32 ZM231.59 374.05 L235.31 379.8
+						 L228.47 379.32 L231.59 374.05 L231.59 374.05 Z" class="st10"/>
+		</g>
+		<g id="shape29-82" v:mID="29" v:groupContext="shape" transform="translate(275.379,-87.6866)">
+			<title>Sheet.29</title>
+			<path d="M2 149.94 L50.05 374.61 L48.05 375.04 L0 150.38 L2 149.94 L2 149.94 ZM51.83 373.18 L50.12 379.8 L45.85 374.47
+						 L51.83 373.18 L51.83 373.18 Z" class="st11"/>
+		</g>
+		<g id="shape30-84" v:mID="30" v:groupContext="shape" transform="translate(276.279,-177.108)">
+			<title>Sheet.30</title>
+			<path d="M0.21 353.74 L229.55 375.85 L229.34 377.89 L0 355.75 L0.21 353.74 L0.21 353.74 ZM228.71 373.72 L234.53 377.35
+						 L228.14 379.8 L228.71 373.72 L228.71 373.72 Z" class="st10"/>
+		</g>
+		<g id="shape31-86" v:mID="31" v:groupContext="shape" transform="translate(275.919,-213.926)">
+			<title>Sheet.31</title>
+			<path d="M0.45 308.72 L312.65 376.06 L312.2 378.04 L0 310.72 L0.45 308.72 L0.45 308.72 ZM312.08 373.84 L317.43 378.13
+						 L310.79 379.8 L312.08 373.84 L312.08 373.84 Z" class="st10"/>
+		</g>
+		<g id="shape32-88" v:mID="32" v:groupContext="shape" transform="translate(275.439,-143.377)">
+			<title>Sheet.32</title>
+			<path d="M1.4 238.41 L150.34 375.59 L148.96 377.09 L0 239.9 L1.4 238.41 L1.4 238.41 ZM150.98 373.41 L153.4 379.8 L146.83
+						 377.9 L150.98 373.41 L150.98 373.41 Z" class="st11"/>
+		</g>
+		<g id="shape33-90" v:mID="33" v:groupContext="shape" transform="translate(275.274,-108.821)">
+			<title>Sheet.33</title>
+			<path d="M1.73 236.53 L90.79 374.97 L89.08 376.07 L0 237.63 L1.73 236.53 L1.73 236.53 ZM91.96 373 L92.7 379.8 L86.82
+						 376.31 L91.96 373 L91.96 373 Z" class="st11"/>
+		</g>
+		<g id="shape34-92" v:mID="34" v:groupContext="shape" transform="translate(275.364,-124.069)">
+			<title>Sheet.34</title>
+			<path d="M1.55 251.66 L108.22 375.28 L106.67 376.61 L0 253 L1.55 251.66 L1.55 251.66 ZM109.1 373.18 L110.78 379.8 L104.46
+						 377.17 L109.1 373.18 L109.1 373.18 Z" class="st11"/>
+		</g>
+		<g id="shape35-94" v:mID="35" v:groupContext="shape" transform="translate(275.154,-87.7165)">
+			<title>Sheet.35</title>
+			<path d="M1.97 215.68 L49.85 374.64 L47.9 375.22 L0 216.27 L1.97 215.68 L1.97 215.68 ZM51.52 373.08 L50.35 379.8 L45.65
+						 374.83 L51.52 373.08 L51.52 373.08 Z" class="st11"/>
+		</g>
+		<g id="shape36-96" v:mID="36" v:groupContext="shape" transform="translate(276.009,-143.736)">
+			<title>Sheet.36</title>
+			<path d="M0.74 320.41 L147.92 376.36 L147.2 378.26 L0 322.32 L0.74 320.41 L0.74 320.41 ZM147.7 374.08 L152.34 379.11
+						 L145.52 379.8 L147.7 374.08 L147.7 374.08 Z" class="st11"/>
+		</g>
+		<g id="shape37-98" v:mID="37" v:groupContext="shape" transform="translate(275.649,-108.821)">
+			<title>Sheet.37</title>
+			<path d="M1.46 285.74 L89.46 375.45 L88 376.87 L0 287.16 L1.46 285.74 L1.46 285.74 ZM90.21 373.29 L92.29 379.8 L85.82
+						 377.57 L90.21 373.29 L90.21 373.29 Z" class="st11"/>
+		</g>
+		<g id="shape38-100" v:mID="38" v:groupContext="shape" transform="translate(275.934,-108.686)">
+			<title>Sheet.38</title>
+			<path d="M0.89 335.24 L87.85 376.57 L86.97 378.41 L0 337.09 L0.89 335.24 L0.89 335.24 ZM87.81 374.29 L92.01 379.67 L85.16
+						 379.8 L87.81 374.29 L87.81 374.29 Z" class="st11"/>
+		</g>
+		<g id="shape39-102" v:mID="39" v:groupContext="shape" transform="translate(275.574,-89.454)">
+			<title>Sheet.39</title>
+			<path d="M1.61 316.29 L48.49 375.18 L46.88 376.45 L0 317.57 L1.61 316.29 L1.61 316.29 ZM49.45 373.11 L50.86 379.8 L44.65
+						 376.91 L49.45 373.11 L49.45 373.11 Z" class="st11"/>
+		</g>
+		<g id="shape40-104" v:mID="40" v:groupContext="shape" transform="translate(276.324,-141.744)">
+			<title>Sheet.40</title>
+			<path d="M0.11 368.21 L146.74 375.79 L146.62 377.83 L0 370.23 L0.11 368.21 L0.11 368.21 ZM145.82 373.71 L151.78 377.08
+						 L145.51 379.8 L145.82 373.71 L145.82 373.71 Z" class="st11"/>
+		</g>
+		<g id="shape41-106" v:mID="41" v:groupContext="shape" transform="translate(230.508,-309.069)">
+			<title>Sheet.41</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st4"/>
+		</g>
+		<g id="shape42-108" v:mID="42" v:groupContext="shape" transform="translate(230.508,-309.069)">
+			<title>Sheet.42</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape43-111" v:mID="43" v:groupContext="shape" transform="translate(233.39,-309.868)">
+			<title>Sheet.43</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape44-115" v:mID="44" v:groupContext="shape" transform="translate(263.764,-309.869)">
+			<title>Sheet.44</title>
+			<desc>4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st13" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>4</text>		</g>
+		<g id="shape45-119" v:mID="45" v:groupContext="shape" transform="translate(230.508,-292.413)">
+			<title>Sheet.45</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape46-121" v:mID="46" v:groupContext="shape" transform="translate(230.508,-292.413)">
+			<title>Sheet.46</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape47-124" v:mID="47" v:groupContext="shape" transform="translate(233.39,-293.221)">
+			<title>Sheet.47</title>
+			<desc>1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text>		</g>
+		<g id="shape48-128" v:mID="48" v:groupContext="shape" transform="translate(230.508,-275.757)">
+			<title>Sheet.48</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st4"/>
+		</g>
+		<g id="shape49-130" v:mID="49" v:groupContext="shape" transform="translate(230.508,-275.757)">
+			<title>Sheet.49</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape50-133" v:mID="50" v:groupContext="shape" transform="translate(233.39,-276.574)">
+			<title>Sheet.50</title>
+			<desc>2</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>2</text>		</g>
+		<g id="shape51-137" v:mID="51" v:groupContext="shape" transform="translate(252.478,-276.574)">
+			<title>Sheet.51</title>
+			<desc>3</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st13" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>3</text>		</g>
+		<g id="shape52-141" v:mID="52" v:groupContext="shape" transform="translate(258.001,-276.574)">
+			<title>Sheet.52</title>
+			<desc>+1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="8.11122" cy="373.835" width="16.23" height="11.9384"/>
+			<path d="M16.22 367.87 L0 367.87 L0 379.8 L16.22 379.8 L16.22 367.87" class="st2"/>
+			<text x="2.44" y="376.82" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>+1</text>		</g>
+		<g id="shape53-145" v:mID="53" v:groupContext="shape" transform="translate(230.508,-259.7)">
+			<title>Sheet.53</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape54-147" v:mID="54" v:groupContext="shape" transform="translate(230.508,-259.7)">
+			<title>Sheet.54</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape55-150" v:mID="55" v:groupContext="shape" transform="translate(233.39,-260.497)">
+			<title>Sheet.55</title>
+			<desc>3</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>3</text>		</g>
+		<g id="shape56-154" v:mID="56" v:groupContext="shape" transform="translate(230.508,-243.164)">
+			<title>Sheet.56</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st4"/>
+		</g>
+		<g id="shape57-156" v:mID="57" v:groupContext="shape" transform="translate(230.508,-243.164)">
+			<title>Sheet.57</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape58-159" v:mID="58" v:groupContext="shape" transform="translate(233.39,-244.053)">
+			<title>Sheet.58</title>
+			<desc>4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>4</text>		</g>
+		<g id="shape59-163" v:mID="59" v:groupContext="shape" transform="translate(263.764,-244.053)">
+			<title>Sheet.59</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st13" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape60-167" v:mID="60" v:groupContext="shape" transform="translate(230.508,-227.107)">
+			<title>Sheet.60</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape61-169" v:mID="61" v:groupContext="shape" transform="translate(230.508,-227.107)">
+			<title>Sheet.61</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape62-172" v:mID="62" v:groupContext="shape" transform="translate(233.39,-227.976)">
+			<title>Sheet.62</title>
+			<desc>5</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>5</text>		</g>
+		<g id="shape63-176" v:mID="63" v:groupContext="shape" transform="translate(230.508,-210.211)">
+			<title>Sheet.63</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape64-178" v:mID="64" v:groupContext="shape" transform="translate(230.508,-210.211)">
+			<title>Sheet.64</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape65-181" v:mID="65" v:groupContext="shape" transform="translate(233.39,-211.085)">
+			<title>Sheet.65</title>
+			<desc>6</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>6</text>		</g>
+		<g id="shape66-185" v:mID="66" v:groupContext="shape" transform="translate(230.508,-193.794)">
+			<title>Sheet.66</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st4"/>
+		</g>
+		<g id="shape67-187" v:mID="67" v:groupContext="shape" transform="translate(230.508,-193.794)">
+			<title>Sheet.67</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape68-190" v:mID="68" v:groupContext="shape" transform="translate(233.39,-194.681)">
+			<title>Sheet.68</title>
+			<desc>7</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>7</text>		</g>
+		<g id="shape69-194" v:mID="69" v:groupContext="shape" transform="translate(263.764,-194.681)">
+			<title>Sheet.69</title>
+			<desc>2</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st13" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>2</text>		</g>
+		<g id="shape70-198" v:mID="70" v:groupContext="shape" transform="translate(230.508,-177.258)">
+			<title>Sheet.70</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape71-200" v:mID="71" v:groupContext="shape" transform="translate(230.508,-177.258)">
+			<title>Sheet.71</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape72-203" v:mID="72" v:groupContext="shape" transform="translate(233.39,-178.117)">
+			<title>Sheet.72</title>
+			<desc>8</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>8</text>		</g>
+		<g id="shape73-207" v:mID="73" v:groupContext="shape" transform="translate(230.508,-160.602)">
+			<title>Sheet.73</title>
+			<path d="M0 363.15 L0 379.8 L41.18 379.8 L41.18 363.15 L0 363.15 L0 363.15 Z" class="st7"/>
+		</g>
+		<g id="shape74-209" v:mID="74" v:groupContext="shape" transform="translate(230.508,-160.602)">
+			<title>Sheet.74</title>
+			<path d="M0 363.15 L41.18 363.15 L41.18 379.8 L0 379.8 L0 363.15" class="st5"/>
+		</g>
+		<g id="shape75-212" v:mID="75" v:groupContext="shape" transform="translate(233.39,-161.505)">
+			<title>Sheet.75</title>
+			<desc>9</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>9</text>		</g>
+		<g id="shape76-216" v:mID="76" v:groupContext="shape" transform="translate(230.508,-143.946)">
+			<title>Sheet.76</title>
+			<path d="M0 363.15 L0 379.8 L41.18 379.8 L41.18 363.15 L0 363.15 L0 363.15 Z" class="st4"/>
+		</g>
+		<g id="shape77-218" v:mID="77" v:groupContext="shape" transform="translate(230.508,-143.946)">
+			<title>Sheet.77</title>
+			<path d="M0 363.15 L41.18 363.15 L41.18 379.8 L0 379.8 L0 363.15" class="st5"/>
+		</g>
+		<g id="shape78-221" v:mID="78" v:groupContext="shape" transform="translate(233.39,-144.841)">
+			<title>Sheet.78</title>
+			<desc>10</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>10</text>		</g>
+		<g id="shape79-225" v:mID="79" v:groupContext="shape" transform="translate(263.764,-144.841)">
+			<title>Sheet.79</title>
+			<desc>1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st13" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text>		</g>
+		<g id="shape80-229" v:mID="80" v:groupContext="shape" transform="translate(230.508,-127.529)">
+			<title>Sheet.80</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape81-231" v:mID="81" v:groupContext="shape" transform="translate(230.508,-127.529)">
+			<title>Sheet.81</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape82-234" v:mID="82" v:groupContext="shape" transform="translate(233.39,-128.329)">
+			<title>Sheet.82</title>
+			<desc>11</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>11</text>		</g>
+		<g id="shape83-238" v:mID="83" v:groupContext="shape" transform="translate(230.508,-110.754)">
+			<title>Sheet.83</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape84-240" v:mID="84" v:groupContext="shape" transform="translate(230.508,-110.754)">
+			<title>Sheet.84</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape85-243" v:mID="85" v:groupContext="shape" transform="translate(233.39,-111.64)">
+			<title>Sheet.85</title>
+			<desc>12</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>12</text>		</g>
+		<g id="shape86-247" v:mID="86" v:groupContext="shape" transform="translate(230.508,-94.9362)">
+			<title>Sheet.86</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape87-249" v:mID="87" v:groupContext="shape" transform="translate(230.508,-94.9362)">
+			<title>Sheet.87</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape88-252" v:mID="88" v:groupContext="shape" transform="translate(233.39,-95.7375)">
+			<title>Sheet.88</title>
+			<desc>…</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.30974" cy="373.835" width="14.62" height="11.9384"/>
+			<path d="M14.62 367.87 L0 367.87 L0 379.8 L14.62 379.8 L14.62 367.87" class="st2"/>
+			<text x="2.34" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>…</text>		</g>
+		<g id="shape89-256" v:mID="89" v:groupContext="shape" transform="translate(230.508,-78.999)">
+			<title>Sheet.89</title>
+			<path d="M0 363.27 L0 379.8 L41.18 379.8 L41.18 363.27 L0 363.27 L0 363.27 Z" class="st7"/>
+		</g>
+		<g id="shape90-258" v:mID="90" v:groupContext="shape" transform="translate(230.508,-78.999)">
+			<title>Sheet.90</title>
+			<path d="M0 363.27 L41.18 363.27 L41.18 379.8 L0 379.8 L0 363.27" class="st5"/>
+		</g>
+		<g id="shape91-261" v:mID="91" v:groupContext="shape" transform="translate(233.39,-79.8525)">
+			<title>Sheet.91</title>
+			<desc>255</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="11.1326" cy="373.835" width="22.27" height="11.9384"/>
+			<path d="M22.27 367.87 L0 367.87 L0 379.8 L22.27 379.8 L22.27 367.87" class="st2"/>
+			<text x="2.84" y="376.82" class="st12" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>255</text>		</g>
+		<g id="shape92-265" v:mID="92" v:groupContext="shape" transform="translate(276.219,-250.503)">
+			<title>Sheet.92</title>
+			<path d="M0.33 311.98 L396.81 375.94 L396.48 377.95 L0 313.99 L0.33 311.98 L0.33 311.98 ZM396.12 373.75 L401.68 377.74
+						 L395.16 379.8 L396.12 373.75 L396.12 373.75 Z" class="st15"/>
+		</g>
+		<g id="shape93-267" v:mID="93" v:groupContext="shape" transform="translate(275.859,-178.426)">
+			<title>Sheet.93</title>
+			<path d="M0.57 305.72 L230.93 376.21 L230.33 378.16 L0 307.67 L0.57 305.72 L0.57 305.72 ZM230.57 373.96 L235.52 378.67
+						 L228.77 379.8 L230.57 373.96 L230.57 373.96 Z" class="st15"/>
+		</g>
+		<g id="shape94-269" v:mID="94" v:groupContext="shape" transform="translate(276.279,-151.285)">
+			<title>Sheet.94</title>
+			<path d="M0.21 379.8 L230.12 353.17 L229.88 351.14 L0 377.8 L0.21 379.8 L0.21 379.8 ZM229.34 355.3 L235.07 351.55 L228.65
+						 349.25 L229.34 355.3 L229.34 355.3 Z" class="st15"/>
+		</g>
+		<g id="shape95-271" v:mID="95" v:groupContext="shape" transform="translate(276.009,-232.679)">
+			<title>Sheet.95</title>
+			<path d="M0.27 327.47 L354.22 375.91 L353.95 377.92 L0 329.48 L0.27 327.47 L0.27 327.47 ZM353.5 373.75 L359.15 377.62
+						 L352.66 379.8 L353.5 373.75 L353.5 373.75 Z" class="st10"/>
+		</g>
+		<g id="shape96-273" v:mID="96" v:groupContext="shape" transform="translate(276.279,-201.134)">
+			<title>Sheet.96</title>
+			<path d="M0.21 379.8 L353.86 348.14 L353.68 346.1 L0 377.77 L0.21 379.8 L0.21 379.8 ZM353.05 350.24 L358.88 346.64 L352.48
+						 344.16 L353.05 350.24 L353.05 350.24 Z" class="st15"/>
+		</g>
+		<g id="shape97-275" v:mID="97" v:groupContext="shape" transform="translate(346.482,-41.2531)">
+			<title>Sheet.97</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st7"/>
+		</g>
+		<g id="shape98-277" v:mID="98" v:groupContext="shape" transform="translate(346.482,-41.2531)">
+			<title>Sheet.98</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape99-280" v:mID="99" v:groupContext="shape" transform="translate(349.371,-91.6514)">
+			<title>Sheet.99</title>
+			<desc>…</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.30974" cy="373.835" width="14.62" height="11.9384"/>
+			<path d="M14.62 367.87 L0 367.87 L0 379.8 L14.62 379.8 L14.62 367.87" class="st2"/>
+			<text x="2.34" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>…</text>		</g>
+		<g id="shape100-284" v:mID="100" v:groupContext="shape" transform="translate(470.019,-94.337)">
+			<title>Sheet.100</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st7"/>
+		</g>
+		<g id="shape101-286" v:mID="101" v:groupContext="shape" transform="translate(470.019,-94.337)">
+			<title>Sheet.101</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape102-289" v:mID="102" v:groupContext="shape" transform="translate(472.925,-144.778)">
+			<title>Sheet.102</title>
+			<desc>5</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>5</text>		</g>
+		<g id="shape103-293" v:mID="103" v:groupContext="shape" transform="translate(511.558,-113.749)">
+			<title>Sheet.103</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape104-295" v:mID="104" v:groupContext="shape" transform="translate(511.558,-113.749)">
+			<title>Sheet.104</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape105-298" v:mID="105" v:groupContext="shape" transform="translate(514.441,-164.138)">
+			<title>Sheet.105</title>
+			<desc>4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>4</text>		</g>
+		<g id="shape106-302" v:mID="106" v:groupContext="shape" transform="translate(542.148,-164.138)">
+			<title>Sheet.106</title>
+			<desc>2</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>2</text>		</g>
+		<g id="shape107-306" v:mID="107" v:groupContext="shape" transform="translate(542.148,-152.155)">
+			<title>Sheet.107</title>
+			<desc>4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>4</text>		</g>
+		<g id="shape108-310" v:mID="108" v:groupContext="shape" transform="translate(536.626,-140.172)">
+			<title>Sheet.108</title>
+			<desc>10</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>10</text>		</g>
+		<g id="shape109-314" v:mID="109" v:groupContext="shape" transform="translate(514.201,-114.441)">
+			<title>Sheet.109</title>
+			<desc>1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text>		</g>
+		<g id="shape110-318" v:mID="110" v:groupContext="shape" transform="translate(519.723,-114.441)">
+			<title>Sheet.110</title>
+			<desc>+4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="8.11122" cy="373.835" width="16.23" height="11.9384"/>
+			<path d="M16.22 367.87 L0 367.87 L0 379.8 L16.22 379.8 L16.22 367.87" class="st2"/>
+			<text x="2.44" y="376.82" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>+4</text>		</g>
+		<g id="shape111-322" v:mID="111" v:groupContext="shape" transform="translate(552.257,-130.525)">
+			<title>Sheet.111</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st7"/>
+		</g>
+		<g id="shape112-324" v:mID="112" v:groupContext="shape" transform="translate(552.257,-130.525)">
+			<title>Sheet.112</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape113-327" v:mID="113" v:groupContext="shape" transform="translate(555.203,-180.952)">
+			<title>Sheet.113</title>
+			<desc>3</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>3</text>		</g>
+		<g id="shape114-331" v:mID="114" v:groupContext="shape" transform="translate(634.615,-169.11)">
+			<title>Sheet.114</title>
+			<path d="M0 313.9 L0 379.8 L41.18 379.8 L41.18 313.9 L0 313.9 L0 313.9 Z" class="st4"/>
+		</g>
+		<g id="shape115-333" v:mID="115" v:groupContext="shape" transform="translate(634.615,-169.11)">
+			<title>Sheet.115</title>
+			<path d="M0 313.9 L41.18 313.9 L41.18 379.8 L0 379.8 L0 313.9" class="st5"/>
+		</g>
+		<g id="shape116-336" v:mID="116" v:groupContext="shape" transform="translate(637.526,-219.595)">
+			<title>Sheet.116</title>
+			<desc>1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text>		</g>
+		<g id="shape117-340" v:mID="117" v:groupContext="shape" transform="translate(665.234,-219.595)">
+			<title>Sheet.117</title>
+			<desc>2</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>2</text>		</g>
+		<g id="shape118-344" v:mID="118" v:groupContext="shape" transform="translate(665.2,-225.489)">
+			<title>Sheet.118</title>
+			<path d="M0 379.32 L0 379.8 L5.52 379.8 L5.52 379.32 L0 379.32 L0 379.32 Z" class="st19"/>
+		</g>
+		<g id="shape119-346" v:mID="119" v:groupContext="shape" transform="translate(665.234,-207.612)">
+			<title>Sheet.119</title>
+			<desc>7</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>7</text>		</g>
+		<g id="shape120-350" v:mID="120" v:groupContext="shape" transform="translate(637.286,-169.898)">
+			<title>Sheet.120</title>
+			<desc>5</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>5</text>		</g>
+		<g id="shape121-354" v:mID="121" v:groupContext="shape" transform="translate(642.809,-169.898)">
+			<title>Sheet.121</title>
+			<desc>-</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="3.49545" cy="373.835" width="7" height="11.9384"/>
+			<path d="M6.99 367.87 L0 367.87 L0 379.8 L6.99 379.8 L6.99 367.87" class="st2"/>
+			<text x="1.84" y="376.82" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>-</text>		</g>
+		<g id="shape122-358" v:mID="122" v:groupContext="shape" transform="translate(646.17,-169.898)">
+			<title>Sheet.122</title>
+			<desc>3</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>3</text>		</g>
+		<g id="shape123-362" v:mID="123" v:groupContext="shape" transform="translate(676.275,-186.725)">
+			<title>Sheet.123</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape124-364" v:mID="124" v:groupContext="shape" transform="translate(676.275,-186.725)">
+			<title>Sheet.124</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape125-367" v:mID="125" v:groupContext="shape" transform="translate(679.141,-237.17)">
+			<title>Sheet.125</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape126-371" v:mID="126" v:groupContext="shape" transform="translate(706.849,-237.17)">
+			<title>Sheet.126</title>
+			<desc>0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0</text>		</g>
+		<g id="shape127-375" v:mID="127" v:groupContext="shape" transform="translate(678.901,-187.474)">
+			<title>Sheet.127</title>
+			<desc>4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>4</text>		</g>
+		<g id="shape128-379" v:mID="128" v:groupContext="shape" transform="translate(304.943,-21.841)">
+			<title>Sheet.128</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape129-381" v:mID="129" v:groupContext="shape" transform="translate(304.943,-21.841)">
+			<title>Sheet.129</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape130-384" v:mID="130" v:groupContext="shape" transform="translate(307.855,-72.2917)">
+			<title>Sheet.130</title>
+			<desc>64</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>64</text>		</g>
+		<g id="shape131-388" v:mID="131" v:groupContext="shape" transform="translate(330.041,-72.2917)">
+			<title>Sheet.131</title>
+			<desc>96</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>96</text>		</g>
+		<g id="shape132-392" v:mID="132" v:groupContext="shape" transform="translate(307.616,-22.5952)">
+			<title>Sheet.132</title>
+			<desc>7</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>7</text>		</g>
+		<g id="shape133-396" v:mID="133" v:groupContext="shape" transform="translate(428.72,-77.4413)">
+			<title>Sheet.133</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape134-398" v:mID="134" v:groupContext="shape" transform="translate(428.72,-77.4413)">
+			<title>Sheet.134</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape135-401" v:mID="135" v:groupContext="shape" transform="translate(431.648,-127.825)">
+			<title>Sheet.135</title>
+			<desc>6</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>6</text>		</g>
+		<g id="shape136-405" v:mID="136" v:groupContext="shape" transform="translate(453.834,-127.825)">
+			<title>Sheet.136</title>
+			<desc>98</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>98</text>		</g>
+		<g id="shape137-409" v:mID="137" v:groupContext="shape" transform="translate(431.409,-78.1289)">
+			<title>Sheet.137</title>
+			<desc>5</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>5</text>		</g>
+		<g id="shape138-413" v:mID="138" v:groupContext="shape" transform="translate(593.796,-149.818)">
+			<title>Sheet.138</title>
+			<path d="M0 313.9 L0 379.8 L41.18 379.8 L41.18 313.9 L0 313.9 L0 313.9 Z" class="st4"/>
+		</g>
+		<g id="shape139-415" v:mID="139" v:groupContext="shape" transform="translate(593.796,-149.818)">
+			<title>Sheet.139</title>
+			<path d="M0 313.9 L41.18 313.9 L41.18 379.8 L0 379.8 L0 313.9" class="st5"/>
+		</g>
+		<g id="shape140-418" v:mID="140" v:groupContext="shape" transform="translate(596.718,-200.312)">
+			<title>Sheet.140</title>
+			<desc>2</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>2</text>		</g>
+		<g id="shape141-422" v:mID="141" v:groupContext="shape" transform="translate(618.904,-200.312)">
+			<title>Sheet.141</title>
+			<desc>99</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>99</text>		</g>
+		<g id="shape142-426" v:mID="142" v:groupContext="shape" transform="translate(596.478,-150.615)">
+			<title>Sheet.142</title>
+			<desc>9</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>9</text>		</g>
+		<g id="shape143-430" v:mID="143" v:groupContext="shape" transform="translate(387.181,-58.0291)">
+			<title>Sheet.143</title>
+			<path d="M0 314.02 L0 379.8 L41.18 379.8 L41.18 314.02 L0 314.02 L0 314.02 Z" class="st4"/>
+		</g>
+		<g id="shape144-432" v:mID="144" v:groupContext="shape" transform="translate(387.181,-58.0291)">
+			<title>Sheet.144</title>
+			<path d="M0 314.02 L41.18 314.02 L41.18 379.8 L0 379.8 L0 314.02" class="st5"/>
+		</g>
+		<g id="shape145-435" v:mID="145" v:groupContext="shape" transform="translate(390.133,-108.466)">
+			<title>Sheet.145</title>
+			<desc>7</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>7</text>		</g>
+		<g id="shape146-439" v:mID="146" v:groupContext="shape" transform="translate(412.318,-108.466)">
+			<title>Sheet.146</title>
+			<desc>97</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="7.95203" cy="373.835" width="15.91" height="11.9384"/>
+			<path d="M15.9 367.87 L0 367.87 L0 379.8 L15.9 379.8 L15.9 367.87" class="st2"/>
+			<text x="2.42" y="376.82" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>97</text>		</g>
+		<g id="shape147-443" v:mID="147" v:groupContext="shape" transform="translate(389.893,-58.7692)">
+			<title>Sheet.147</title>
+			<desc>6</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.77151" cy="373.835" width="9.55" height="11.9384"/>
+			<path d="M9.54 367.87 L0 367.87 L0 379.8 L9.54 379.8 L9.54 367.87" class="st2"/>
+			<text x="2.01" y="376.82" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>6</text>		</g>
+		<g id="shape148-447" v:mID="148" v:groupContext="shape" transform="translate(31.8163,-277.674)">
+			<title>Sheet.148</title>
+			<path d="M0 362.83 C0 360.95 1.52 359.43 3.41 359.43 L68.51 359.43 C70.4 359.43 71.91 360.95 71.91 362.83 L71.91 376.41
+						 C71.91 378.28 70.4 379.8 68.51 379.8 L3.41 379.8 C1.52 379.8 0 378.28 0 376.41 L0 362.83 Z" class="st4"/>
+		</g>
+		<g id="shape149-449" v:mID="149" v:groupContext="shape" transform="translate(31.8163,-277.674)">
+			<title>Sheet.149</title>
+			<path d="M0 362.83 C0 360.95 1.52 359.43 3.41 359.43 L68.51 359.43 C70.4 359.43 71.91 360.95 71.91 362.83 L71.91 376.41
+						 C71.91 378.28 70.4 379.8 68.51 379.8 L3.41 379.8 C1.52 379.8 0 378.28 0 376.41 L0 362.83 Z" class="st21"/>
+		</g>
+		<g id="shape150-451" v:mID="150" v:groupContext="shape" transform="translate(36,-278.851)">
+			<title>Sheet.150</title>
+			<desc>Insert key</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="35.613" cy="372.612" width="71.23" height="14.3829"/>
+			<path d="M71.23 365.42 L0 365.42 L0 379.8 L71.23 379.8 L71.23 365.42" class="st2"/>
+			<text x="9.64" y="376.21" class="st22" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Insert key    </text>		</g>
+		<g id="shape151-455" v:mID="151" v:groupContext="shape" transform="translate(47.7236,-257.004)">
+			<title>Sheet.151</title>
+			<path d="M0 369.44 L9.81 369.44 L9.81 359.07 L29.44 359.07 L29.44 369.44 L39.26 369.44 L19.63 379.8 L0 369.44 Z"
+					class="st23"/>
+		</g>
+		<g id="shape152-457" v:mID="152" v:groupContext="shape" transform="translate(31.8163,-236.094)">
+			<title>Sheet.152</title>
+			<path d="M0 362.73 C0 360.85 1.54 359.31 3.42 359.31 L68.49 359.31 C70.38 359.31 71.91 360.85 71.91 362.73 L71.91 376.39
+						 C71.91 378.28 70.38 379.8 68.49 379.8 L3.42 379.8 C1.54 379.8 0 378.28 0 376.39 L0 362.73 Z" class="st4"/>
+		</g>
+		<g id="shape153-459" v:mID="153" v:groupContext="shape" transform="translate(31.8163,-236.094)">
+			<title>Sheet.153</title>
+			<path d="M0 362.73 C0 360.85 1.54 359.31 3.42 359.31 L68.49 359.31 C70.38 359.31 71.91 360.85 71.91 362.73 L71.91 376.39
+						 C71.91 378.28 70.38 379.8 68.49 379.8 L3.42 379.8 C1.54 379.8 0 378.28 0 376.39 L0 362.73 Z" class="st21"/>
+		</g>
+		<g id="shape154-461" v:mID="154" v:groupContext="shape" transform="translate(54.6845,-237.332)">
+			<title>Sheet.154</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.8573" cy="372.612" width="33.72" height="14.3829"/>
+			<path d="M33.71 365.42 L0 365.42 L0 379.8 L33.71 379.8 L33.71 365.42" class="st2"/>
+			<text x="3.86" y="376.21" class="st22" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape155-465" v:mID="155" v:groupContext="shape" transform="translate(23.0522,-195.232)">
+			<title>Sheet.155</title>
+			<path d="M0 363.53 C0 361.73 1.46 360.27 3.26 360.27 L87.15 360.27 C88.95 360.27 90.4 361.73 90.4 363.53 L90.4 376.55
+						 C90.4 378.35 88.95 379.8 87.15 379.8 L3.26 379.8 C1.46 379.8 0 378.35 0 376.55 L0 363.53 Z" class="st4"/>
+		</g>
+		<g id="shape156-467" v:mID="156" v:groupContext="shape" transform="translate(23.0522,-195.232)">
+			<title>Sheet.156</title>
+			<path d="M0 363.53 C0 361.73 1.46 360.27 3.26 360.27 L87.15 360.27 C88.95 360.27 90.4 361.73 90.4 363.53 L90.4 376.55
+						 C90.4 378.35 88.95 379.8 87.15 379.8 L3.26 379.8 C1.46 379.8 0 378.35 0 376.55 L0 363.53 Z" class="st21"/>
+		</g>
+		<g id="shape157-469" v:mID="157" v:groupContext="shape" transform="translate(27,-196.017)">
+			<title>Sheet.157</title>
+			<desc>0x0102ABCD</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.6644" cy="372.612" width="87.33" height="14.3829"/>
+			<path d="M87.33 365.42 L0 365.42 L0 379.8 L87.33 379.8 L87.33 365.42" class="st2"/>
+			<text x="7.36" y="376.21" class="st22" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0102ABCD</text>		</g>
+		<g id="shape158-473" v:mID="158" v:groupContext="shape" transform="translate(47.7236,-214.824)">
+			<title>Sheet.158</title>
+			<path d="M0 369.5 L9.81 369.5 L9.81 359.19 L29.44 359.19 L29.44 369.5 L39.26 369.5 L19.63 379.8 L0 369.5 Z"
+					class="st23"/>
+		</g>
+		<g id="shape159-475" v:mID="159" v:groupContext="shape" transform="translate(49.8539,-181.212)">
+			<title>Sheet.159</title>
+			<path d="M11.89 368.42 C11.89 371.57 11.47 374.11 10.94 374.11 L6.9 374.11 C6.37 374.11 5.94 376.67 5.94 379.8 C5.94
+						 376.67 5.52 374.11 5 374.11 L0.95 374.11 C0.43 374.11 0 371.57 0 368.42" class="st24"/>
+		</g>
+		<g id="shape160-478" v:mID="160" v:groupContext="shape" transform="translate(64.2606,-180.973)">
+			<title>Sheet.160</title>
+			<path d="M9.54 368.54 C9.54 371.66 9.21 374.17 8.79 374.17 L5.53 374.17 C5.11 374.17 4.77 376.7 4.77 379.8 C4.77 376.7
+						 4.43 374.17 4.02 374.17 L0.76 374.17 C0.34 374.17 0 371.66 0 368.54" class="st24"/>
+		</g>
+		<g id="shape161-481" v:mID="161" v:groupContext="shape" transform="translate(18.19,-60.9649)">
+			<title>Sheet.161</title>
+			<path d="M0 354.74 C0 351.97 2.25 349.73 5.03 349.73 L10.77 349.73 L30.27 267.14 L26.92 349.73 L59.58 349.73 C62.35 349.73
+						 64.59 351.97 64.59 354.74 L64.59 354.74 L64.59 362.26 L64.59 374.8 C64.59 377.57 62.35 379.8 59.58 379.8
+						 L26.92 379.8 L10.77 379.8 L10.77 379.8 L5.03 379.8 C2.25 379.8 0 377.57 0 374.8 L0 362.26 L0 354.74 L0 354.74
+						 Z" class="st23"/>
+		</g>
+		<g id="shape162-483" v:mID="162" v:groupContext="shape" transform="translate(28.141,-66.9569)">
+			<title>Sheet.162</title>
+			<desc>chunk id</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="27.5794" cy="372.612" width="55.16" height="14.3829"/>
+			<path d="M55.16 365.42 L0 365.42 L0 379.8 L55.16 379.8 L55.16 365.42" class="st2"/>
+			<text x="5.26" y="376.21" class="st25" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>chunk id</text>		</g>
+		<g id="shape163-487" v:mID="163" v:groupContext="shape" transform="translate(50.8451,-112.132)">
+			<title>Sheet.163</title>
+			<path d="M0 354.64 C0 351.87 2.27 349.61 5.04 349.61 L10.74 349.61 L16.27 313.66 L26.86 349.61 L59.43 349.61 C62.22 349.61
+						 64.47 351.87 64.47 354.64 L64.47 354.64 L64.47 362.19 L64.47 374.77 C64.47 377.56 62.22 379.8 59.43 379.8
+						 L26.86 379.8 L10.74 379.8 L10.74 379.8 L5.04 379.8 C2.27 379.8 0 377.56 0 374.77 L0 362.19 L0 354.64 L0
+						 354.64 Z" class="st23"/>
+		</g>
+		<g id="shape164-489" v:mID="164" v:groupContext="shape" transform="translate(68.8168,-118.181)">
+			<title>Sheet.164</title>
+			<desc>bin id</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="18.3881" cy="372.612" width="36.78" height="14.3829"/>
+			<path d="M36.78 365.42 L0 365.42 L0 379.8 L36.78 379.8 L36.78 365.42" class="st2"/>
+			<text x="4.06" y="376.21" class="st25" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>bin id</text>		</g>
+		<g id="shape165-493" v:mID="165" v:groupContext="shape" transform="translate(113.454,-225.085)">
+			<title>Sheet.165</title>
+			<path d="M0.01 375.68 L13.23 375.73 L13.22 377.77 L0 377.72 L0.01 375.68 L0.01 375.68 ZM12.22 373.69 L18.33 376.76 L12.2
+						 379.8 L12.22 373.69 L12.22 373.69 Z" class="st26"/>
+		</g>
+		<g id="shape166-495" v:mID="166" v:groupContext="shape" transform="translate(200.975,-280.969)">
+			<title>Sheet.166</title>
+			<path d="M0 375.73 L20.11 375.73 L20.11 377.77 L0 377.77 L0 375.73 L0 375.73 ZM19.09 373.69 L25.21 376.75 L19.09 379.8
+						 L19.09 373.69 L19.09 373.69 Z" class="st26"/>
+		</g>
+		<g id="shape167-497" v:mID="167" v:groupContext="shape" transform="translate(275.739,-179.745)">
+			<title>Sheet.167</title>
+			<path d="M0.81 274.59 L231.38 376.48 L230.54 378.37 L0 276.48 L0.81 274.59 L0.81 274.59 ZM231.26 374.2 L235.64 379.47
+						 L228.8 379.8 L231.26 374.2 L231.26 374.2 Z" class="st27"/>
+		</g>
+		<g id="shape168-499" v:mID="168" v:groupContext="shape" transform="translate(521.823,-96.8834)">
+			<title>Sheet.168</title>
+			<path d="M127.17 309.02 L127.17 378.79 C127.17 379.35 126.72 379.8 126.15 379.8 L3.06 379.8 C2.52 379.8 2.04 379.35 2.04
+						 378.79 L2.04 369.59 L4.08 369.59 L4.08 378.79 L3.06 377.77 L126.15 377.77 L125.13 378.79 L125.13 309.02
+						 L127.17 309.02 ZM0 370.61 L3.06 364.5 L6.12 370.61 L0 370.61 Z" class="st28"/>
+		</g>
+		<g id="shape169-501" v:mID="169" v:groupContext="shape" transform="translate(478.603,-39.7553)">
+			<title>Sheet.169</title>
+			<path d="M0 347.57 C0 344.01 2.91 341.1 6.48 341.1 L237.86 341.1 C241.43 341.1 244.31 344.01 244.31 347.57 L244.31 373.36
+						 C244.31 376.93 241.43 379.8 237.86 379.8 L6.48 379.8 C2.91 379.8 0 376.93 0 373.36 L0 347.57 Z"
+					class="st23"/>
+		</g>
+		<g id="shape170-503" v:mID="170" v:groupContext="shape" transform="translate(487.717,-45.5378)">
+			<title>Sheet.170</title>
+			<desc>Move bin from group 1 to 4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="126.387" cy="369.018" width="252.78" height="21.5726"/>
+			<path d="M252.77 358.23 L0 358.23 L0 379.8 L252.77 379.8 L252.77 358.23" class="st2"/>
+			<text x="18.98" y="374.41" class="st29" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Move bin from group 1 to 4</text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i2.svg b/doc/guides/prog_guide/img/efd_i2.svg
new file mode 100644
index 0000000..a5f43f9
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i2.svg
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i2.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="2.85156in" height="2.98777in"
+		viewBox="0 0 205.313 215.12" xml:space="preserve" color-interpolation-filters="sRGB" class="st18">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {visibility:visible}
+		.st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st3 {fill:#deebf6;stroke:#c7c8c8;stroke-width:0.25}
+		.st4 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st5 {fill:#ff0000;stroke:#c7c8c8;stroke-width:0.25}
+		.st6 {fill:none;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st7 {fill:none;stroke:#0070c0;stroke-width:1.5}
+		.st8 {marker-end:url(#mrkr5-91);stroke:#0070c0;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st9 {fill:#0070c0;fill-opacity:1;stroke:#0070c0;stroke-opacity:1;stroke-width:0.37313432835821}
+		.st10 {fill:none;stroke:none;stroke-width:0.25}
+		.st11 {fill:#ff0000;font-family:Calibri;font-size:1.00001em;font-weight:bold}
+		.st12 {font-size:1em}
+		.st13 {marker-end:url(#mrkr5-101);stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st14 {fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1;stroke-width:0.28409090909091}
+		.st15 {fill:#5b9bd5;font-family:Calibri;font-size:1.00001em;font-weight:bold}
+		.st16 {marker-end:url(#mrkr5-110);stroke:#41719c;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st17 {fill:#41719c;fill-opacity:1;stroke:#41719c;stroke-opacity:1;stroke-width:0.28409090909091}
+		.st18 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-91" class="st9" v:arrowType="5" v:arrowSize="2" v:setback="4.45" refX="-4.45" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+		</marker>
+		<marker id="mrkr5-101" class="st14" v:arrowType="5" v:arrowSize="2" v:setback="6.16" refX="-6.16" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
+		</marker>
+		<marker id="mrkr5-110" class="st17" v:arrowType="5" v:arrowSize="2" v:setback="6.16" refX="-6.16" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
+		</marker>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape2-1" v:mID="2" v:groupContext="shape" transform="translate(24.4044,-42.7174)">
+			<title>Circle</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow2-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 138.62 A76.5 76.5 0 0 1 153 138.62 A76.5 76.5 0 1 1 0 138.62 Z" class="st2"/>
+			</g>
+			<path d="M0 138.62 A76.5 76.5 0 0 1 153 138.62 A76.5 76.5 0 1 1 0 138.62 Z" class="st3"/>
+		</g>
+		<g id="shape3-6" v:mID="3" v:groupContext="shape" transform="translate(24.4044,-144.53)">
+			<title>Circle.3</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow3-7" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape4-11" v:mID="4" v:groupContext="shape" transform="translate(21.0294,-102.342)">
+			<title>Circle.4</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow4-12" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape5-16" v:mID="5" v:groupContext="shape" transform="translate(69.4044,-183.342)">
+			<title>Circle.5</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow5-17" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape6-21" v:mID="6" v:groupContext="shape" transform="translate(117.217,-183.342)">
+			<title>Circle.6</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow6-22" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st5"/>
+		</g>
+		<g id="shape7-26" v:mID="7" v:groupContext="shape" transform="translate(171.217,-104.03)">
+			<title>Circle.7</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow7-27" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st5"/>
+		</g>
+		<g id="shape8-31" v:mID="8" v:groupContext="shape" transform="translate(109.904,-38.2174)">
+			<title>Circle.8</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow8-32" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st5"/>
+		</g>
+		<g id="shape9-36" v:mID="9" v:groupContext="shape" transform="translate(21.0294,-124.842)">
+			<title>Circle.9</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow9-37" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st5"/>
+		</g>
+		<g id="shape10-41" v:mID="10" v:groupContext="shape" transform="translate(147.029,-168.717)">
+			<title>Circle.10</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow10-42" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape11-46" v:mID="11" v:groupContext="shape" transform="translate(138.029,-48.3424)">
+			<title>Circle.11</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow11-47" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape12-51" v:mID="12" v:groupContext="shape" transform="translate(160.529,-74.2174)">
+			<title>Circle.12</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow12-52" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape13-56" v:mID="13" v:groupContext="shape" transform="translate(40.7169,-57.3424)">
+			<title>Circle.13</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow13-57" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape14-61" v:mID="14" v:groupContext="shape" transform="translate(42.4044,-168.717)">
+			<title>Circle.14</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow14-62" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape15-66" v:mID="15" v:groupContext="shape" transform="translate(66.0294,-42.7174)">
+			<title>Circle.15</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow15-67" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape16-71" v:mID="16" v:groupContext="shape" transform="translate(25.5294,-79.8424)">
+			<title>Circle.16</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow16-72" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape17-76" v:mID="17" v:groupContext="shape" transform="translate(165.029,-143.405)">
+			<title>Circle.17</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow17-77" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st2"/>
+			</g>
+			<path d="M0 208.93 A6.1875 6.1875 0 1 1 12.37 208.93 A6.1875 6.1875 0 0 1 0 208.93 Z" class="st4"/>
+		</g>
+		<g id="shape18-81" v:mID="18" v:groupContext="shape" transform="translate(276.618,4.50201) rotate(45)">
+			<title>Ellipse</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow18-82" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,1.63935,1.1506)" class="st1">
+				<path d="M0 187.01 A14.7383 28.1086 0 1 1 29.48 187.01 A14.7383 28.1086 0 1 1 0 187.01 Z" class="st6"/>
+			</g>
+			<path d="M0 187.01 A14.7383 28.1086 0 1 1 29.48 187.01 A14.7383 28.1086 0 1 1 0 187.01 Z" class="st7"/>
+		</g>
+		<g id="shape19-86" v:mID="19" v:groupContext="shape" transform="translate(251.273,355.436) rotate(156.038)">
+			<title>Sheet.19</title>
+			<path d="M-0 215.12 A73.4538 31.2572 85.43 0 1 40.92 208.96 L41.1 209.27" class="st8"/>
+		</g>
+		<g id="shape20-92" v:mID="20" v:groupContext="shape" transform="translate(62.705,-78.7174)">
+			<title>Sheet.20</title>
+			<desc>Target Hashed Value</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="42.6994" cy="203.87" width="85.4" height="22.5"/>
+			<rect x="0" y="192.62" width="85.3987" height="22.5" class="st10"/>
+			<text x="6.73" y="200.27" class="st11" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Target Hashed <tspan
+						x="28.48" dy="1.2em" class="st12">Value</tspan></text>		</g>
+		<g id="shape21-96" v:mID="21" v:groupContext="shape" transform="translate(314.101,88.728) rotate(75.9638)">
+			<title>Sheet.21</title>
+			<path d="M0 215.12 L16.92 215.12" class="st13"/>
+		</g>
+		<g id="shape23-102" v:mID="23" v:groupContext="shape" transform="translate(60.4044,-138.342)">
+			<title>Sheet.23</title>
+			<desc>Keys</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="24.75" cy="203.87" width="49.5" height="22.5"/>
+			<rect x="0" y="192.62" width="49.5" height="22.5" class="st10"/>
+			<text x="13.21" y="207.47" class="st15" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Keys</text>		</g>
+		<g id="shape24-105" v:mID="24" v:groupContext="shape" transform="translate(-125.293,114.034) rotate(-104.574)">
+			<title>Sheet.24</title>
+			<path d="M0 215.12 L22.9 215.12" class="st16"/>
+		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i3.svg b/doc/guides/prog_guide/img/efd_i3.svg
new file mode 100644
index 0000000..ae22903
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i3.svg
@@ -0,0 +1,634 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i3.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="6.56036in" height="5.44284in"
+		viewBox="0 0 472.346 391.884" xml:space="preserve" color-interpolation-filters="sRGB" class="st22">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false"/>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {visibility:visible}
+		.st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st4 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
+		.st5 {marker-end:url(#mrkr5-24);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st6 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.28409090909091}
+		.st7 {fill:none;stroke:#2e75b5;stroke-width:1}
+		.st8 {fill:#5b9bd5;font-family:Calibri;font-size:1.00001em}
+		.st9 {font-size:1em}
+		.st10 {fill:none;stroke:none;stroke-width:1}
+		.st11 {fill:#feffff;font-family:Calibri;font-size:1.00001em;font-weight:bold}
+		.st12 {fill:#5b9bd5;fill-opacity:0.25;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.25}
+		.st13 {fill:#4f87bb;stroke:#40709c;stroke-width:0.75}
+		.st14 {fill:#feffff;font-family:Calibri;font-size:0.75em}
+		.st15 {fill:none;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st16 {fill:none;stroke:#2e75b5;stroke-width:2.25}
+		.st17 {stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st18 {fill:#305497;stroke:#2e75b5;stroke-width:1}
+		.st19 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:none}
+		.st20 {fill:#92d050;fill-opacity:0.3;stroke:none;stroke-width:0.25}
+		.st21 {fill:#feffff;font-family:Calibri;font-size:1.16666em}
+		.st22 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-24" class="st6" v:arrowType="5" v:arrowSize="2" v:setback="6.16" refX="-6.16" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
+		</marker>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<v:layer v:name="Connector" v:index="0"/>
+		<g id="shape2-1" v:mID="2" v:groupContext="shape" transform="translate(111.25,-354.482)">
+			<title>Rectangle</title>
+			<desc>Packet Header</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="42.75" cy="382.884" width="85.5" height="18"/>
+			<g id="shadow2-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="85.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="85.5" height="18" class="st3"/>
+			<text x="13.24" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Packet Header</text>		</g>
+		<g id="shape3-7" v:mID="3" v:groupContext="shape" transform="translate(192.25,-354.482)">
+			<title>Rectangle.3</title>
+			<desc>Payload</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="382.884" width="108" height="18"/>
+			<g id="shadow3-8" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="108" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="108" height="18" class="st3"/>
+			<text x="37.95" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Payload</text>		</g>
+		<g id="shape4-13" v:mID="4" v:groupContext="shape" transform="translate(136,-311.232)">
+			<title>Rectangle.4</title>
+			<desc>Flow Key</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="27" cy="382.884" width="54" height="18"/>
+			<g id="shadow4-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="54" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="54" height="18" class="st3"/>
+			<text x="8.87" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Flow Key</text>		</g>
+		<g id="shape5-19" v:mID="5" v:groupContext="shape" transform="translate(465.501,-160.057) rotate(59.7436)">
+			<title>Sheet.5</title>
+			<path d="M0 391.88 L25.1 391.88" class="st5"/>
+		</g>
+		<g id="shape8-25" v:mID="8" v:groupContext="shape" transform="translate(219.25,-320.169)">
+			<title>Sheet.8</title>
+			<desc>Fields of the packet are used to form a flow Key</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="377.822" width="135" height="28.125"/>
+			<rect x="0" y="363.759" width="135" height="28.125" class="st7"/>
+			<text x="10.7" y="374.22" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Fields of the packet are <tspan
+						x="9.67" dy="1.2em" class="st9">used to form a flow Key</tspan></text>		</g>
+		<g id="group13-29" transform="translate(120.25,-266.897)" v:mID="13" v:groupContext="group">
+			<title>Sheet.13</title>
+			<g id="shape11-30" v:mID="11" v:groupContext="shape" transform="translate(85.5,751.143) rotate(180)">
+				<title>Trapezoid</title>
+				<v:userDefs>
+					<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				</v:userDefs>
+				<g id="shadow11-31" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,-0.345598,-1.97279)" class="st1">
+					<path d="M0 391.88 L85.5 391.88 L60.19 359.26 L25.31 359.26 L0 391.88 Z" class="st2"/>
+				</g>
+				<path d="M0 391.88 L85.5 391.88 L60.19 359.26 L25.31 359.26 L0 391.88 Z" class="st3"/>
+			</g>
+			<g id="shape12-35" v:mID="12" v:groupContext="shape" transform="translate(13.5,-6.525)">
+				<title>Sheet.12</title>
+				<desc>H(..)</desc>
+				<v:textBlock v:margins="rect(4,4,4,4)"/>
+				<v:textRect cx="27" cy="381.689" width="54" height="20.3906"/>
+				<rect x="0" y="371.494" width="54" height="20.3906" class="st10"/>
+				<text x="16.27" y="385.29" class="st11" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H(..)</text>			</g>
+		</g>
+		<g id="shape14-38" v:mID="14" v:groupContext="shape" transform="translate(-229.872,96.3648) rotate(-90.0429)">
+			<title>Simple Arrow</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ArrowType" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<g id="shadow14-39" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,-1.97305,0.344122)" class="st1">
+				<path d="M0 391.88 L10.18 387.38 L10.18 389.63 L16.71 389.63 L16.71 391.88 L16.71 394.13 L10.18 394.13 L10.18 396.38
+							 L0 391.88 Z" class="st12"/>
+			</g>
+			<path d="M0 391.88 L10.18 387.38 L10.18 389.63 L16.71 389.63 L16.71 391.88 L16.71 394.13 L10.18 394.13 L10.18 396.38
+						 L0 391.88 Z" class="st13"/>
+		</g>
+		<g id="shape15-43" v:mID="15" v:groupContext="shape" transform="translate(212.5,-271.46)">
+			<title>Sheet.15</title>
+			<desc>Hash function is used to create a flow table index</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="377.822" width="135" height="28.125"/>
+			<rect x="0" y="363.759" width="135" height="28.125" class="st7"/>
+			<text x="9.05" y="374.22" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Hash function is used to <tspan
+						x="7.39" dy="1.2em" class="st9">create a flow table index</tspan></text>		</g>
+		<g id="shape58-47" v:mID="58" v:groupContext="shape" transform="translate(199,-221.397)">
+			<title>Rectangle.58</title>
+			<desc>Key 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow58-48" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>		</g>
+		<g id="shape59-53" v:mID="59" v:groupContext="shape" transform="translate(232.75,-221.397)">
+			<title>Rectangle.59</title>
+			<desc>Action 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="382.884" width="42.75" height="18"/>
+			<g id="shadow59-54" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>		</g>
+		<g id="shape60-59" v:mID="60" v:groupContext="shape" transform="translate(280,-221.397)">
+			<title>Rectangle.60</title>
+			<desc>Key 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow60-60" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>		</g>
+		<g id="shape61-65" v:mID="61" v:groupContext="shape" transform="translate(313.75,-221.397)">
+			<title>Rectangle.61</title>
+			<desc>Action 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="382.884" width="42.75" height="18"/>
+			<g id="shadow61-66" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>		</g>
+		<g id="shape62-71" v:mID="62" v:groupContext="shape" transform="translate(361,-221.397)">
+			<title>Rectangle.62</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow62-72" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape63-76" v:mID="63" v:groupContext="shape" transform="translate(394.75,-221.397)">
+			<title>Rectangle.63</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow63-77" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape64-81" v:mID="64" v:groupContext="shape" transform="translate(199,-198.897)">
+			<title>Rectangle.64</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow64-82" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape65-86" v:mID="65" v:groupContext="shape" transform="translate(232.75,-198.897)">
+			<title>Rectangle.65</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow65-87" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape66-91" v:mID="66" v:groupContext="shape" transform="translate(280,-198.897)">
+			<title>Rectangle.66</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow66-92" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape67-96" v:mID="67" v:groupContext="shape" transform="translate(313.75,-198.897)">
+			<title>Rectangle.67</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow67-97" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape68-101" v:mID="68" v:groupContext="shape" transform="translate(361,-198.897)">
+			<title>Rectangle.68</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow68-102" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape69-106" v:mID="69" v:groupContext="shape" transform="translate(394.75,-198.897)">
+			<title>Rectangle.69</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow69-107" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape70-111" v:mID="70" v:groupContext="shape" transform="translate(199,-162.897)">
+			<title>Rectangle.70</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow70-112" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape71-117" v:mID="71" v:groupContext="shape" transform="translate(232.75,-162.897)">
+			<title>Rectangle.71</title>
+			<desc>Action x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="382.884" width="42.75" height="18"/>
+			<g id="shadow71-118" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+			<text x="4.99" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>		</g>
+		<g id="shape72-123" v:mID="72" v:groupContext="shape" transform="translate(280,-162.897)">
+			<title>Rectangle.72</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow72-124" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape73-129" v:mID="73" v:groupContext="shape" transform="translate(313.75,-162.897)">
+			<title>Rectangle.73</title>
+			<desc>Action y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="382.884" width="42.75" height="18"/>
+			<g id="shadow73-130" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+			<text x="4.89" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>		</g>
+		<g id="shape74-135" v:mID="74" v:groupContext="shape" transform="translate(361,-162.897)">
+			<title>Rectangle.74</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow74-136" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="shape75-141" v:mID="75" v:groupContext="shape" transform="translate(394.75,-162.897)">
+			<title>Rectangle.75</title>
+			<desc>Action z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="382.884" width="42.75" height="18"/>
+			<g id="shadow75-142" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+			<text x="5.18" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>		</g>
+		<g id="shape76-147" v:mID="76" v:groupContext="shape" transform="translate(199,-126.397)">
+			<title>Rectangle.76</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow76-148" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape77-152" v:mID="77" v:groupContext="shape" transform="translate(232.75,-126.397)">
+			<title>Rectangle.77</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow77-153" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape78-157" v:mID="78" v:groupContext="shape" transform="translate(280,-126.397)">
+			<title>Rectangle.78</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow78-158" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape79-162" v:mID="79" v:groupContext="shape" transform="translate(313.75,-126.397)">
+			<title>Rectangle.79</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow79-163" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape80-167" v:mID="80" v:groupContext="shape" transform="translate(361,-126.397)">
+			<title>Rectangle.80</title>
+			<desc>Key N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow80-168" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="5.21" y="385.58" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>		</g>
+		<g id="shape81-173" v:mID="81" v:groupContext="shape" transform="translate(394.75,-126.397)">
+			<title>Rectangle.81</title>
+			<desc>Action N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="382.884" width="42.75" height="18"/>
+			<g id="shadow81-174" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="42.75" height="18" class="st3"/>
+			<text x="5.67" y="385.58" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>		</g>
+		<g id="shape82-179" v:mID="82" v:groupContext="shape" transform="translate(196.75,-117.397)">
+			<title>Rectangle.82</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow82-180" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="256.384" width="245.25" height="135.5" class="st15"/>
+			</g>
+			<rect x="0" y="256.384" width="245.25" height="135.5" class="st16"/>
+		</g>
+		<g id="shape83-184" v:mID="83" v:groupContext="shape" transform="translate(554.884,123.862) rotate(90)">
+			<title>Sheet.83</title>
+			<path d="M0 391.88 L99 391.88" class="st17"/>
+		</g>
+		<g id="shape84-187" v:mID="84" v:groupContext="shape" transform="translate(208,-248.397)">
+			<title>Sheet.84</title>
+			<desc>Load Balancing Flow Table</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="91.75" cy="386.259" width="183.5" height="11.25"/>
+			<rect x="0" y="380.634" width="183.5" height="11.25" class="st18"/>
+			<text x="26.14" y="389.86" class="st11" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Load Balancing Flow Table</text>		</g>
+		<g id="shape85-190" v:mID="85" v:groupContext="shape" transform="translate(190,-157.835)">
+			<title>Rectangle.85</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow85-191" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="363.759" width="261" height="28.125" class="st19"/>
+			</g>
+			<rect x="0" y="363.759" width="261" height="28.125" class="st20"/>
+		</g>
+		<g id="shape86-195" v:mID="86" v:groupContext="shape" transform="translate(163,-169.022)">
+			<title>Sheet.86</title>
+			<path d="M0 391.88 L18.76 391.88" class="st5"/>
+		</g>
+		<g id="shape87-200" v:mID="87" v:groupContext="shape" transform="translate(19,-198.107)">
+			<title>Sheet.87</title>
+			<desc>Hash value used to index Flow table</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="377.822" width="135" height="28.125"/>
+			<rect x="0" y="363.759" width="135" height="28.125" class="st7"/>
+			<text x="6.79" y="374.22" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Hash value used to index <tspan
+						x="42.16" dy="1.2em" class="st9">Flow table</tspan></text>		</g>
+		<g id="shape88-204" v:mID="88" v:groupContext="shape" transform="translate(551.381,21.2928) rotate(87.9001)">
+			<title>Sheet.88</title>
+			<path d="M0 391.88 L20.86 391.88" class="st5"/>
+		</g>
+		<g id="shape89-209" v:mID="89" v:groupContext="shape" transform="translate(494.785,297.309) rotate(131.987)">
+			<title>Sheet.89</title>
+			<path d="M0 391.88 L30.84 391.88" class="st5"/>
+		</g>
+		<g id="shape90-214" v:mID="90" v:groupContext="shape" transform="translate(228.25,-92.5847)">
+			<title>Rectangle.90</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow90-215" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape91-220" v:mID="91" v:groupContext="shape" transform="translate(340.75,-92.5847)">
+			<title>Rectangle.91</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow91-221" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="group96-226" transform="translate(253,-51.4597)" v:mID="96" v:groupContext="group">
+			<title>Sheet.96</title>
+			<g id="shape97-227" v:mID="97" v:groupContext="shape" transform="translate(85.5,751.143) rotate(180)">
+				<title>Trapezoid</title>
+				<v:userDefs>
+					<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				</v:userDefs>
+				<g id="shadow97-228" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,-0.345598,-1.97279)" class="st1">
+					<path d="M0 391.88 L85.5 391.88 L60.19 359.26 L25.31 359.26 L0 391.88 Z" class="st2"/>
+				</g>
+				<path d="M0 391.88 L85.5 391.88 L60.19 359.26 L25.31 359.26 L0 391.88 Z" class="st3"/>
+			</g>
+			<g id="shape98-232" v:mID="98" v:groupContext="shape" transform="translate(13.5,-6.525)">
+				<title>Sheet.98</title>
+				<desc>Match</desc>
+				<v:textBlock v:margins="rect(4,4,4,4)"/>
+				<v:textRect cx="27" cy="381.689" width="54" height="20.3906"/>
+				<rect x="0" y="371.494" width="54" height="20.3906" class="st10"/>
+				<text x="10.98" y="385.29" class="st11" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Match</text>			</g>
+		</g>
+		<g id="shape99-235" v:mID="99" v:groupContext="shape" transform="translate(532.137,0.00916548) rotate(54.6508)">
+			<title>Sheet.99</title>
+			<path d="M0 391.88 L93.23 391.88" class="st5"/>
+		</g>
+		<g id="shape100-240" v:mID="100" v:groupContext="shape" transform="translate(683.134,224.487) rotate(90)">
+			<title>Sheet.100</title>
+			<path d="M0 391.88 L77.15 391.88" class="st5"/>
+		</g>
+		<g id="shape101-245" v:mID="101" v:groupContext="shape" transform="translate(692.213,476.024) rotate(129.078)">
+			<title>Sheet.101</title>
+			<path d="M0 391.88 L95.37 391.88" class="st5"/>
+		</g>
+		<g id="shape102-250" v:mID="102" v:groupContext="shape" transform="translate(293.5,-97.0847)">
+			<title>Rectangle.102</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="382.884" width="31.5" height="18"/>
+			<g id="shadow102-251" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape103-256" v:mID="103" v:groupContext="shape" transform="translate(169.75,-55.9597)">
+			<title>Rectangle.103</title>
+			<desc>Flow Key</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="27" cy="382.884" width="54" height="18"/>
+			<g id="shadow103-257" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="54" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="54" height="18" class="st3"/>
+			<text x="8.87" y="385.88" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Flow Key</text>		</g>
+		<g id="shape104-262" v:mID="104" v:groupContext="shape" transform="translate(226,-64.9597)">
+			<title>Sheet.104</title>
+			<path d="M0 391.88 L34.34 391.88" class="st5"/>
+		</g>
+		<g id="shape105-267" v:mID="105" v:groupContext="shape" transform="translate(54,-82.4597)">
+			<title>Sheet.105</title>
+			<desc>Retrieved keys are matched with input key</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="377.822" width="135" height="28.125"/>
+			<rect x="0" y="363.759" width="135" height="28.125" class="st7"/>
+			<text x="22.51" y="374.22" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Retrieved keys are <tspan
+						x="9.83" dy="1.2em" class="st9">matched with input key</tspan></text>		</g>
+		<g id="shape106-271" v:mID="106" v:groupContext="shape" transform="translate(271,-23.9597)">
+			<title>Rectangle.106</title>
+			<desc>Action</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="27" cy="382.884" width="54" height="18"/>
+			<g id="shadow106-272" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="373.884" width="54" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="373.884" width="54" height="18" class="st3"/>
+			<text x="8.67" y="387.08" class="st21" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action</text>		</g>
+		<g id="shape111-277" v:mID="111" v:groupContext="shape" transform="translate(-94.8716,350.902) rotate(-90.0429)">
+			<title>Simple Arrow.111</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ArrowType" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<g id="shadow111-278" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,-1.97305,0.344122)" class="st1">
+				<path d="M0 391.88 L10.18 387.38 L10.18 389.63 L16.71 389.63 L16.71 391.88 L16.71 394.13 L10.18 394.13 L10.18 396.38
+							 L0 391.88 Z" class="st12"/>
+			</g>
+			<path d="M0 391.88 L10.18 387.38 L10.18 389.63 L16.71 389.63 L16.71 391.88 L16.71 394.13 L10.18 394.13 L10.18 396.38
+						 L0 391.88 Z" class="st13"/>
+		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i4.svg b/doc/guides/prog_guide/img/efd_i4.svg
new file mode 100644
index 0000000..5be5ccd
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i4.svg
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i4.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="2.78993in" height="1.78151in"
+		viewBox="0 0 200.875 128.269" xml:space="preserve" color-interpolation-filters="sRGB" class="st19">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {fill:none;stroke:none;stroke-width:0.25}
+		.st2 {fill:#5b9bd5;font-family:Calibri;font-size:0.75em}
+		.st3 {font-size:1em}
+		.st4 {fill:#5b9bd5;font-family:Calibri;font-size:0.75em;font-weight:bold}
+		.st5 {fill:#deebf6;stroke:none;stroke-width:0.25}
+		.st6 {stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st7 {stroke:#5b9bd5;stroke-dasharray:0.75,1.5;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st8 {fill:#ff0000;font-size:1em}
+		.st9 {baseline-shift:-28.8834%;font-size:0.577667em}
+		.st10 {fill:#ff0000;font-family:Calibri;font-size:0.75em}
+		.st11 {fill:#5b9bd5;font-size:1em}
+		.st12 {visibility:visible}
+		.st13 {fill:#5b9bd5;fill-opacity:0.25;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.25}
+		.st14 {fill:url(#grad0-73);stroke:#40709c;stroke-width:0.75}
+		.st15 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
+		.st16 {fill:#00fefe;font-size:1em}
+		.st17 {fill:#00b050}
+		.st18 {stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st19 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Patterns_And_Gradients">
+		<linearGradient id="grad0-73" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(250 0.5 0.5)">
+			<stop offset="0" stop-color="#4f87bb" stop-opacity="1"/>
+			<stop offset="0.48" stop-color="#4f87bb" stop-opacity="1"/>
+			<stop offset="0.82" stop-color="#5b9bd5" stop-opacity="1"/>
+		</linearGradient>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape2-1" v:mID="2" v:groupContext="shape" transform="translate(18.25,-59.3478)">
+			<title>Sheet.2</title>
+			<desc>Key 1 Key 2 ... Key 28</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="18" cy="121.519" width="36" height="13.5"/>
+			<rect x="0" y="114.769" width="36" height="13.5" class="st1"/>
+			<text x="8.09" y="108.02" class="st2" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1<v:newlineChar/><tspan
+						x="8.09" dy="1.2em" class="st3">Key </tspan>2<v:newlineChar/><tspan x="14.59" dy="1.2em" class="st3">...<v:newlineChar/></tspan><tspan
+						x="5.81" dy="1.2em" class="st3">Key </tspan>28</text>		</g>
+		<g id="shape9-7" v:mID="9" v:groupContext="shape" transform="translate(52,-91.9728)">
+			<title>Sheet.9</title>
+			<desc>Target Value</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="17.4375" cy="122.644" width="34.88" height="11.25"/>
+			<rect x="0" y="117.019" width="34.875" height="11.25" class="st1"/>
+			<text x="5.43" y="119.94" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Target <tspan x="6.77"
+						dy="1.2em" class="st3">Value</tspan></text>		</g>
+		<g id="shape11-11" v:mID="11" v:groupContext="shape" transform="translate(52,-42.4728)">
+			<title>Sheet.11</title>
+			<desc>0 1 0</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="17.4375" cy="105.769" width="34.88" height="45"/>
+			<rect x="0" y="83.2689" width="34.875" height="45" class="st5"/>
+			<text x="15.16" y="92.27" class="st2" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0<v:newlineChar/><tspan
+						x="15.16" dy="1.2em" class="st3">1<v:newlineChar/><v:newlineChar/></tspan><tspan x="15.16" dy="2.4em"
+						class="st3">0</tspan></text>		</g>
+		<g id="shape8-16" v:mID="8" v:groupContext="shape" transform="translate(180.269,21.6711) rotate(90)">
+			<title>Sheet.8</title>
+			<path d="M0 128.27 L69.75 128.27" class="st6"/>
+		</g>
+		<g id="shape10-19" v:mID="10" v:groupContext="shape" transform="translate(215.144,21.6711) rotate(90)">
+			<title>Sheet.10</title>
+			<path d="M0 128.27 L69.75 128.27" class="st6"/>
+		</g>
+		<g id="shape4-22" v:mID="4" v:groupContext="shape" transform="translate(22.75,-77.3478)">
+			<title>Sheet.4</title>
+			<path d="M0 128.27 L157.5 128.27" class="st7"/>
+		</g>
+		<g id="shape5-25" v:mID="5" v:groupContext="shape" transform="translate(23.875,-66.0978)">
+			<title>Sheet.5</title>
+			<path d="M0 128.27 L158.62 128.27" class="st7"/>
+		</g>
+		<g id="shape6-28" v:mID="6" v:groupContext="shape" transform="translate(22.75,-54.8478)">
+			<title>Sheet.6</title>
+			<path d="M0 128.27 L159.75 128.27" class="st7"/>
+		</g>
+		<g id="shape7-31" v:mID="7" v:groupContext="shape" transform="translate(22.75,-87.4728)">
+			<title>Sheet.7</title>
+			<path d="M0 128.27 L155.25 128.27" class="st6"/>
+		</g>
+		<g id="shape12-34" v:mID="12" v:groupContext="shape" transform="translate(91.9375,-42.4728)">
+			<title>Sheet.12</title>
+			<desc>0 0 0</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="8.4375" cy="105.769" width="16.88" height="45"/>
+			<rect x="0" y="83.2689" width="16.875" height="45" class="st1"/>
+			<text x="6.16" y="92.27" class="st2" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0<v:newlineChar/><tspan
+						x="6.16" dy="1.2em" class="st8">0<v:newlineChar/><v:newlineChar/></tspan><tspan x="6.16" dy="2.4em"
+						class="st3">0</tspan></text>		</g>
+		<g id="shape26-39" v:mID="26" v:groupContext="shape" transform="translate(86.875,-88.5978)">
+			<title>Sheet.26</title>
+			<desc>H1(x)</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="14.0625" cy="122.644" width="28.13" height="11.25"/>
+			<rect x="0" y="117.019" width="28.125" height="11.25" class="st1"/>
+			<text x="5.03" y="125.34" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H<tspan dy="-0.284em"
+						class="st9" v:baseFontSize="8">1</tspan><tspan dy="0.164em" class="st3">(</tspan>x)</text>		</g>
+		<g id="shape27-44" v:mID="27" v:groupContext="shape" transform="translate(115,-42.4728)">
+			<title>Sheet.27</title>
+			<desc>1 1 0</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="8.4375" cy="105.769" width="16.88" height="45"/>
+			<rect x="0" y="83.2689" width="16.875" height="45" class="st1"/>
+			<text x="6.16" y="92.27" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1<v:newlineChar/><tspan
+						x="6.16" dy="1.2em" class="st11">1<v:newlineChar/><v:newlineChar/></tspan><tspan x="6.16" dy="2.4em"
+						class="st11">0</tspan></text>		</g>
+		<g id="shape28-49" v:mID="28" v:groupContext="shape" transform="translate(109.938,-88.5978)">
+			<title>Sheet.28</title>
+			<desc>H2(x)</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="14.0625" cy="122.644" width="28.13" height="11.25"/>
+			<rect x="0" y="117.019" width="28.125" height="11.25" class="st1"/>
+			<text x="5.03" y="125.34" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H<tspan dy="-0.284em"
+						class="st9" v:baseFontSize="8">2</tspan><tspan dy="0.164em" class="st3">(</tspan>x)</text>		</g>
+		<g id="shape29-54" v:mID="29" v:groupContext="shape" transform="translate(155.5,-42.4728)">
+			<title>Sheet.29</title>
+			<desc>0 1 0</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="8.4375" cy="105.769" width="16.88" height="45"/>
+			<rect x="0" y="83.2689" width="16.875" height="45" class="st1"/>
+			<text x="6.16" y="92.27" class="st2" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0<v:newlineChar/><tspan
+						x="6.16" dy="1.2em" class="st3">1<v:newlineChar/><v:newlineChar/></tspan><tspan x="6.16" dy="2.4em"
+						class="st3">0</tspan></text>		</g>
+		<g id="shape30-59" v:mID="30" v:groupContext="shape" transform="translate(150.438,-88.5978)">
+			<title>Sheet.30</title>
+			<desc>Hm(x)</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="14.0625" cy="122.644" width="28.13" height="11.25"/>
+			<rect x="0" y="117.019" width="28.125" height="11.25" class="st1"/>
+			<text x="4.24" y="125.34" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H<tspan dy="-0.284em"
+						class="st9" v:baseFontSize="8">m</tspan><tspan dy="0.164em" class="st3">(</tspan>x)</text>		</g>
+		<g id="shape31-64" v:mID="31" v:groupContext="shape" transform="translate(130.188,-89.7228)">
+			<title>Sheet.31</title>
+			<desc>…..</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="14.0625" cy="122.644" width="28.13" height="11.25"/>
+			<rect x="0" y="117.019" width="28.125" height="11.25" class="st1"/>
+			<text x="8.46" y="125.34" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>…..</text>		</g>
+		<g id="shape32-67" v:mID="32" v:groupContext="shape" transform="translate(34,-23.3478)">
+			<title>Sheet.32</title>
+			<desc>Store m for this group of keys</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="66.375" cy="122.644" width="132.75" height="11.25"/>
+			<g id="shadow32-68" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st12">
+				<rect x="0" y="117.019" width="132.75" height="11.25" class="st13"/>
+			</g>
+			<rect x="0" y="117.019" width="132.75" height="11.25" class="st14"/>
+			<text x="6.32" y="125.64" class="st15" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Store <tspan
+						class="st16">m</tspan> for this group of keys</text>		</g>
+		<g id="shape36-76" v:mID="36" v:groupContext="shape" transform="translate(159.381,-100.964)">
+			<title>Sheet.36</title>
+			<path d="M3.45 125.81 L6.87 119.34 L7.99 120.16 L3.87 128.27 L0 124.35 L0.86 123.13 L3.45 125.81 Z" class="st17"/>
+		</g>
+		<g id="group44-79" transform="translate(97.5625,-100.086)" v:mID="44" v:groupContext="group">
+			<title>Sheet.44</title>
+			<g id="shape42-80" v:mID="42" v:groupContext="shape" transform="translate(85.4972,28.6255) rotate(41.8011)">
+				<title>Sheet.42</title>
+				<path d="M0 128.27 L6.04 128.27" class="st18"/>
+			</g>
+			<g id="shape43-83" v:mID="43" v:groupContext="shape" transform="translate(-87.9035,34.8564) rotate(-43.2597)">
+				<title>Sheet.43</title>
+				<path d="M0 128.27 L5.87 128.27" class="st18"/>
+			</g>
+		</g>
+		<g id="group45-86" transform="translate(120.625,-100.086)" v:mID="45" v:groupContext="group">
+			<title>Sheet.45</title>
+			<g id="shape46-87" v:mID="46" v:groupContext="shape" transform="translate(85.4972,28.6255) rotate(41.8011)">
+				<title>Sheet.46</title>
+				<path d="M0 128.27 L6.04 128.27" class="st18"/>
+			</g>
+			<g id="shape47-90" v:mID="47" v:groupContext="shape" transform="translate(-87.9035,34.8564) rotate(-43.2597)">
+				<title>Sheet.47</title>
+				<path d="M0 128.27 L5.87 128.27" class="st18"/>
+			</g>
+		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i5.svg b/doc/guides/prog_guide/img/efd_i5.svg
new file mode 100644
index 0000000..b6540ba
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i5.svg
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i5.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.34375in" height="2.86443in"
+		viewBox="0 0 600.75 206.239" xml:space="preserve" color-interpolation-filters="sRGB" class="st14">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {visibility:visible}
+		.st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st4 {fill:#feffff;font-family:Calibri;font-size:1.5em}
+		.st5 {fill:#feffff;font-family:Calibri;font-size:1.16666em}
+		.st6 {marker-end:url(#mrkr5-36);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st7 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
+		.st8 {stroke:#5b9bd5;stroke-dasharray:1.5,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st9 {fill:none;stroke:none;stroke-width:0.25}
+		.st10 {fill:#5b9bd5;font-family:Calibri;font-size:1.5em;font-weight:bold}
+		.st11 {baseline-shift:-32.4951%;font-size:0.649902em}
+		.st12 {fill:#deebf6;stroke:#0070c0;stroke-width:1}
+		.st13 {fill:#5b9bd5;font-family:Calibri;font-size:1.5em}
+		.st14 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-36" class="st7" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+		</marker>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape2-1" v:mID="2" v:groupContext="shape" transform="translate(93.0294,-158.5)">
+			<title>Rectangle</title>
+			<desc>All Keys</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="216" cy="192.739" width="432" height="27"/>
+			<g id="shadow2-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="179.239" width="432" height="27" class="st2"/>
+			</g>
+			<rect x="0" y="179.239" width="432" height="27" class="st3"/>
+			<text x="187.88" y="198.14" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>All Keys</text>		</g>
+		<g id="shape3-7" v:mID="3" v:groupContext="shape" transform="translate(21.0294,-77.5)">
+			<title>Rectangle.3</title>
+			<desc>Group 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="188.239" width="108" height="36"/>
+			<g id="shadow3-8" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="170.239" width="108" height="36" class="st2"/>
+			</g>
+			<rect x="0" y="170.239" width="108" height="36" class="st3"/>
+			<text x="30.97" y="192.44" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group 1</text>		</g>
+		<g id="shape4-13" v:mID="4" v:groupContext="shape" transform="translate(156.029,-77.5)">
+			<title>Rectangle.4</title>
+			<desc>Group 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="188.239" width="108" height="36"/>
+			<g id="shadow4-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="170.239" width="108" height="36" class="st2"/>
+			</g>
+			<rect x="0" y="170.239" width="108" height="36" class="st3"/>
+			<text x="30.97" y="192.44" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group 2</text>		</g>
+		<g id="shape5-19" v:mID="5" v:groupContext="shape" transform="translate(291.029,-77.5)">
+			<title>Rectangle.5</title>
+			<desc>Group 3</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="188.239" width="108" height="36"/>
+			<g id="shadow5-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="170.239" width="108" height="36" class="st2"/>
+			</g>
+			<rect x="0" y="170.239" width="108" height="36" class="st3"/>
+			<text x="30.97" y="192.44" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group 3</text>		</g>
+		<g id="shape6-25" v:mID="6" v:groupContext="shape" transform="translate(471.029,-77.5)">
+			<title>Rectangle.6</title>
+			<desc>Group X</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="188.239" width="108" height="36"/>
+			<g id="shadow6-26" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="170.239" width="108" height="36" class="st2"/>
+			</g>
+			<rect x="0" y="170.239" width="108" height="36" class="st3"/>
+			<text x="30.88" y="192.44" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group X</text>		</g>
+		<g id="shape7-31" v:mID="7" v:groupContext="shape" transform="translate(359.05,247.819) rotate(165.964)">
+			<title>Sheet.7</title>
+			<path d="M0 206.24 L178.5 206.24" class="st6"/>
+		</g>
+		<g id="shape8-37" v:mID="8" v:groupContext="shape" transform="translate(428.903,215.562) rotate(144.462)">
+			<title>Sheet.8</title>
+			<path d="M0 206.24 L70.39 206.24" class="st6"/>
+		</g>
+		<g id="shape9-42" v:mID="9" v:groupContext="shape" transform="translate(470.075,-81.0976) rotate(51.3402)">
+			<title>Sheet.9</title>
+			<path d="M0 206.24 L50.59 206.24" class="st6"/>
+		</g>
+		<g id="shape10-47" v:mID="10" v:groupContext="shape" transform="translate(364.228,-150.976) rotate(15.5241)">
+			<title>Sheet.10</title>
+			<path d="M0 206.24 L161.1 206.24" class="st6"/>
+		</g>
+		<g id="shape11-52" v:mID="11" v:groupContext="shape" transform="translate(408.029,-95.5)">
+			<title>Sheet.11</title>
+			<path d="M0 206.24 L45 206.24" class="st8"/>
+		</g>
+		<g id="shape12-55" v:mID="12" v:groupContext="shape" transform="translate(48.0294,-50.5)">
+			<title>Sheet.12</title>
+			<desc>H7</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="22.5" cy="192.739" width="45" height="27"/>
+			<rect x="0" y="179.239" width="45" height="27" class="st9"/>
+			<text x="13.86" y="198.14" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H<tspan
+						dy="-0.284em" class="st11" v:baseFontSize="18">7</tspan></text>		</g>
+		<g id="shape13-59" v:mID="13" v:groupContext="shape" transform="translate(192.029,-50.5)">
+			<title>Sheet.13</title>
+			<desc>H267</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="22.5" cy="192.739" width="45" height="27"/>
+			<rect x="0" y="179.239" width="45" height="27" class="st9"/>
+			<text x="7.93" y="198.14" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H<tspan dy="-0.284em"
+						class="st11" v:baseFontSize="18">267</tspan></text>		</g>
+		<g id="shape14-63" v:mID="14" v:groupContext="shape" transform="translate(318.029,-50.5)">
+			<title>Sheet.14</title>
+			<desc>H46</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="22.5" cy="192.739" width="45" height="27"/>
+			<rect x="0" y="179.239" width="45" height="27" class="st9"/>
+			<text x="10.89" y="198.14" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H<tspan
+						dy="-0.284em" class="st11" v:baseFontSize="18">46</tspan></text>		</g>
+		<g id="shape15-67" v:mID="15" v:groupContext="shape" transform="translate(502.529,-50.5)">
+			<title>Sheet.15</title>
+			<desc>H132</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="22.5" cy="192.739" width="45" height="27"/>
+			<rect x="0" y="179.239" width="45" height="27" class="st9"/>
+			<text x="7.93" y="198.14" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>H<tspan dy="-0.284em"
+						class="st11" v:baseFontSize="18">132</tspan></text>		</g>
+		<g id="shape16-71" v:mID="16" v:groupContext="shape" transform="translate(111.029,-19)">
+			<title>Sheet.16</title>
+			<desc>Store hash function index for each group of keys</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="189" cy="192.739" width="378" height="27"/>
+			<rect x="0" y="179.239" width="378" height="27" class="st12"/>
+			<text x="12.27" y="198.14" class="st13" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Store hash function index for each group of keys</text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i6.svg b/doc/guides/prog_guide/img/efd_i6.svg
new file mode 100644
index 0000000..9aee30b
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i6.svg
@@ -0,0 +1,1254 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i6.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.2496in" height="5.89673in"
+		viewBox="0 0 593.971 424.565" xml:space="preserve" color-interpolation-filters="sRGB" class="st27">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {visibility:visible}
+		.st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st4 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
+		.st5 {fill:#feffff;font-family:Calibri;font-size:0.75em}
+		.st6 {fill:none;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st7 {fill:none;stroke:#2e75b5;stroke-width:2.25}
+		.st8 {fill:#305497;stroke:#2e75b5;stroke-width:1}
+		.st9 {fill:#feffff;font-family:Calibri;font-size:0.833336em;font-weight:bold}
+		.st10 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2)}
+		.st11 {fill:#5b9bd5}
+		.st12 {stroke:#c7c8c8;stroke-width:0.25}
+		.st13 {fill:#acccea;stroke:#c7c8c8;stroke-width:0.25}
+		.st14 {fill:#feffff;font-family:Calibri;font-size:1.00001em;font-weight:bold}
+		.st15 {fill:#ed7d31;stroke:#c7c8c8;stroke-width:0.25}
+		.st16 {fill:#deebf6;stroke:#c7c8c8;stroke-width:0.25}
+		.st17 {marker-end:url(#mrkr5-212);stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+		.st18 {fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1;stroke-width:0.28409090909091}
+		.st19 {fill:none;stroke:#2e75b5;stroke-width:1}
+		.st20 {fill:#5b9bd5;font-family:Calibri;font-size:1.00001em}
+		.st21 {fill:none;stroke:none;stroke-width:0.25}
+		.st22 {font-size:1em}
+		.st23 {fill:#ffffff}
+		.st24 {stroke:#5b9bd5;stroke-dasharray:1.5,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st25 {marker-end:url(#mrkr5-444);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+		.st26 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
+		.st27 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Markers">
+		<g id="lend5">
+			<path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+		</g>
+		<marker id="mrkr5-212" class="st18" v:arrowType="5" v:arrowSize="2" v:setback="5.8" refX="-5.8" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
+		</marker>
+		<marker id="mrkr5-444" class="st26" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
+				markerUnits="strokeWidth" overflow="visible">
+			<use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+		</marker>
+	</defs>
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(319.501,-335.688)">
+			<title>Rectangle.58</title>
+			<desc>Key 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow3-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>		</g>
+		<g id="shape4-7" v:mID="4" v:groupContext="shape" transform="translate(353.251,-335.688)">
+			<title>Rectangle.59</title>
+			<desc>Action 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow4-8" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>		</g>
+		<g id="shape5-13" v:mID="5" v:groupContext="shape" transform="translate(400.501,-335.688)">
+			<title>Rectangle.60</title>
+			<desc>Key 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow5-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>		</g>
+		<g id="shape6-19" v:mID="6" v:groupContext="shape" transform="translate(434.251,-335.688)">
+			<title>Rectangle.61</title>
+			<desc>Action 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow6-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>		</g>
+		<g id="shape7-25" v:mID="7" v:groupContext="shape" transform="translate(481.501,-335.688)">
+			<title>Rectangle.62</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow7-26" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape8-30" v:mID="8" v:groupContext="shape" transform="translate(515.251,-335.688)">
+			<title>Rectangle.63</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow8-31" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape9-35" v:mID="9" v:groupContext="shape" transform="translate(319.501,-313.188)">
+			<title>Rectangle.64</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow9-36" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape10-40" v:mID="10" v:groupContext="shape" transform="translate(353.251,-313.188)">
+			<title>Rectangle.65</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow10-41" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape11-45" v:mID="11" v:groupContext="shape" transform="translate(400.501,-313.188)">
+			<title>Rectangle.66</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow11-46" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape12-50" v:mID="12" v:groupContext="shape" transform="translate(434.251,-313.188)">
+			<title>Rectangle.67</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow12-51" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape13-55" v:mID="13" v:groupContext="shape" transform="translate(481.501,-313.188)">
+			<title>Rectangle.68</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow13-56" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape14-60" v:mID="14" v:groupContext="shape" transform="translate(515.251,-313.188)">
+			<title>Rectangle.69</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow14-61" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape15-65" v:mID="15" v:groupContext="shape" transform="translate(319.501,-277.188)">
+			<title>Rectangle.70</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow15-66" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape16-71" v:mID="16" v:groupContext="shape" transform="translate(353.251,-277.188)">
+			<title>Rectangle.71</title>
+			<desc>Action x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow16-72" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>		</g>
+		<g id="shape17-77" v:mID="17" v:groupContext="shape" transform="translate(400.501,-277.188)">
+			<title>Rectangle.72</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow17-78" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape18-83" v:mID="18" v:groupContext="shape" transform="translate(434.251,-277.188)">
+			<title>Rectangle.73</title>
+			<desc>Action y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow18-84" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>		</g>
+		<g id="shape19-89" v:mID="19" v:groupContext="shape" transform="translate(481.501,-277.188)">
+			<title>Rectangle.74</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow19-90" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="shape20-95" v:mID="20" v:groupContext="shape" transform="translate(515.251,-277.188)">
+			<title>Rectangle.75</title>
+			<desc>Action z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow20-96" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>		</g>
+		<g id="shape21-101" v:mID="21" v:groupContext="shape" transform="translate(319.501,-240.687)">
+			<title>Rectangle.76</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow21-102" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape22-106" v:mID="22" v:groupContext="shape" transform="translate(353.251,-240.687)">
+			<title>Rectangle.77</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow22-107" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape23-111" v:mID="23" v:groupContext="shape" transform="translate(400.501,-240.687)">
+			<title>Rectangle.78</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow23-112" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape24-116" v:mID="24" v:groupContext="shape" transform="translate(434.251,-240.687)">
+			<title>Rectangle.79</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow24-117" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape25-121" v:mID="25" v:groupContext="shape" transform="translate(481.501,-240.687)">
+			<title>Rectangle.80</title>
+			<desc>Key N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow25-122" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>		</g>
+		<g id="shape26-127" v:mID="26" v:groupContext="shape" transform="translate(515.251,-240.687)">
+			<title>Rectangle.81</title>
+			<desc>Action N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow26-128" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>		</g>
+		<g id="shape27-133" v:mID="27" v:groupContext="shape" transform="translate(317.251,-231.687)">
+			<title>Rectangle.82</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow27-134" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+			</g>
+			<rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+		</g>
+		<g id="shape28-138" v:mID="28" v:groupContext="shape" transform="translate(328.501,-362.688)">
+			<title>Sheet.28</title>
+			<desc>Local Table for N Specific Flows Serviced at Node 1</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+			<rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+			<text x="5.77" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node 1</text>		</g>
+		<g id="group34-141" transform="translate(66.0294,-165.569)" v:mID="34" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Device)"/>
+				<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Load balancer)"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Load balancer</title>
+			<g id="shape35-142" v:mID="35" v:groupContext="shape" transform="translate(0,-7.33146)">
+				<title>Sheet.35</title>
+				<g id="shadow35-143" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st10"/>
+					<path d="M0 377.86 L72 377.86" class="st6"/>
+					<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st6"/>
+				</g>
+				<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st11"/>
+				<path d="M0 377.86 L72 377.86" class="st12"/>
+				<path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st12"/>
+			</g>
+			<g id="shape36-152" v:mID="36" v:groupContext="shape" transform="translate(8.03054,-12.9324)">
+				<title>Sheet.36</title>
+				<g id="shadow36-153" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97
+								 413.34 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83
+								 L51.34 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37
+								 L38.13 400.48 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16
+								 L46.49 392.01 ZM27.71 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42
+								 27.71 415.42 C32.75 415.42 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71
+								 400.04 C31.15 400.04 33.96 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54
+								 21.46 409.74 21.46 406.29 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56
+								 L11.6 407.56 L8.62 410.51 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z"
+							class="st2"/>
+				</g>
+				<path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97 413.34
+							 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83 L51.34
+							 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37 L38.13 400.48
+							 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16 L46.49 392.01 ZM27.71
+							 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42 27.71 415.42 C32.75 415.42
+							 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71 400.04 C31.15 400.04 33.96
+							 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54 21.46 409.74 21.46 406.29
+							 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56 L11.6 407.56 L8.62 410.51
+							 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z" class="st13"/>
+			</g>
+		</g>
+		<g id="shape37-157" v:mID="37" v:groupContext="shape" transform="translate(21.0294,-45.4375)">
+			<title>Rectangle</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow37-158" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="336.433" width="135" height="88.1315" class="st2"/>
+			</g>
+			<rect x="0" y="336.433" width="135" height="88.1315" class="st3"/>
+		</g>
+		<g id="shape38-162" v:mID="38" v:groupContext="shape" transform="translate(34.693,-126.438)">
+			<title>Sheet.38</title>
+			<desc>EFD Table</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="49.3364" cy="415.565" width="98.68" height="18"/>
+			<rect x="0" y="406.565" width="98.6728" height="18" class="st8"/>
+			<text x="24.87" y="419.17" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>EFD Table</text>		</g>
+		<g id="shape39-165" v:mID="39" v:groupContext="shape" transform="translate(30.0294,-99.4375)">
+			<title>Rectangle.39</title>
+			<desc>Group_id</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+			<g id="shadow39-166" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+			<text x="7.87" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group_id</text>		</g>
+		<g id="shape40-171" v:mID="40" v:groupContext="shape" transform="translate(93.193,-99.4375)">
+			<title>Rectangle.40</title>
+			<desc>Hash index</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+			<g id="shadow40-172" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+			<text x="4.64" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Hash index</text>		</g>
+		<g id="shape41-177" v:mID="41" v:groupContext="shape" transform="translate(30.193,-82.4275)">
+			<title>Rectangle.41</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow41-178" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape42-182" v:mID="42" v:groupContext="shape" transform="translate(30.193,-66.8125)">
+			<title>Rectangle.42</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow42-183" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape43-187" v:mID="43" v:groupContext="shape" transform="translate(30.1112,-52.1875)">
+			<title>Rectangle.43</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow43-188" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape44-192" v:mID="44" v:groupContext="shape" transform="translate(93.0294,-81.4375)">
+			<title>Rectangle.44</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow44-193" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape45-197" v:mID="45" v:groupContext="shape" transform="translate(93.193,-66.8125)">
+			<title>Rectangle.45</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow45-198" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape46-202" v:mID="46" v:groupContext="shape" transform="translate(93.193,-52.1875)">
+			<title>Rectangle.46</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow46-203" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+			</g>
+			<rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+		</g>
+		<g id="shape47-207" v:mID="47" v:groupContext="shape" transform="translate(374.924,544.022) rotate(135)">
+			<title>Sheet.47</title>
+			<path d="M-0 417.75 A40.674 18.0151 -156.2 0 0 40.24 422.15 L40.49 421.89" class="st17"/>
+		</g>
+		<g id="shape48-213" v:mID="48" v:groupContext="shape" transform="translate(21.0294,-19)">
+			<title>Sheet.48</title>
+			<desc>Supports X*N Flows</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+			<rect x="0" y="406.565" width="135" height="18" class="st19"/>
+			<text x="19.05" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports X*N Flows</text>		</g>
+		<g id="shape49-216" v:mID="49" v:groupContext="shape" transform="translate(48.0294,-229.938)">
+			<title>Sheet.49</title>
+			<desc>Frontend Server or Load Balancer</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="400.94" width="108" height="47.25"/>
+			<rect x="0" y="377.315" width="108" height="47.25" class="st21"/>
+			<text x="14.56" y="397.34" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Frontend Server<v:newlineChar/><tspan
+						x="13.16" dy="1.2em" class="st22">or Load Balancer </tspan> </text>		</g>
+		<g id="group51-220" transform="translate(223.876,-310.938)" v:mID="51" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server</title>
+			<g id="shape52-221" v:mID="52" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.52</title>
+				<g id="shadow52-222" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape53-226" v:mID="53" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.53</title>
+				<g id="shadow53-227" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape54-231" v:mID="54" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.54</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow54-232" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape59-239" v:mID="59" v:groupContext="shape" transform="translate(277.876,-373.938)">
+			<title>Sheet.59</title>
+			<path d="M-0 424.56 A111.108 53.2538 42.31 0 1 93.83 421.21 L94.14 421.41" class="st17"/>
+		</g>
+		<g id="shape60-244" v:mID="60" v:groupContext="shape" transform="translate(205.876,-283.938)">
+			<title>Sheet.60</title>
+			<desc>Backend Server 1</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 1</text>		</g>
+		<g id="group61-247" transform="translate(223.876,-207.438)" v:mID="61" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server.61</title>
+			<g id="shape62-248" v:mID="62" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.62</title>
+				<g id="shadow62-249" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape63-253" v:mID="63" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.63</title>
+				<g id="shadow63-254" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape64-258" v:mID="64" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.64</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow64-259" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape65-266" v:mID="65" v:groupContext="shape" transform="translate(205.876,-180.437)">
+			<title>Sheet.65</title>
+			<desc>Backend Server 2</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 2</text>		</g>
+		<g id="group66-269" transform="translate(219.029,-58.9375)" v:mID="66" v:groupContext="group">
+			<v:custProps>
+				<v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+						v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+						v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+				<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+				<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+				<v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+						v:ask="false" v:langID="1033" v:cal="0"/>
+			</v:custProps>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+				<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+				<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+				<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+			</v:userDefs>
+			<title>Server.66</title>
+			<g id="shape67-270" v:mID="67" v:groupContext="shape" transform="translate(13.0183,0)">
+				<title>Sheet.67</title>
+				<g id="shadow67-271" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+				</g>
+				<rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+			</g>
+			<g id="shape68-275" v:mID="68" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+				<title>Sheet.68</title>
+				<g id="shadow68-276" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+				</g>
+				<ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+			</g>
+			<g id="shape69-280" v:mID="69" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+				<title>Sheet.69</title>
+				<v:userDefs>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+					<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+				</v:userDefs>
+				<g id="shadow69-281" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+						transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+					<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+								 L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+							class="st10"/>
+				</g>
+				<path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+							 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+			</g>
+		</g>
+		<g id="shape70-288" v:mID="70" v:groupContext="shape" transform="translate(201.029,-26.056)">
+			<title>Sheet.70</title>
+			<desc>Backend Server X</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+			<rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+			<text x="11.86" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server X</text>		</g>
+		<g id="shape71-291" v:mID="71" v:groupContext="shape" transform="translate(684.44,239.627) rotate(90)">
+			<title>Sheet.71</title>
+			<path d="M0 424.56 L45 424.56" class="st24"/>
+		</g>
+		<g id="shape72-294" v:mID="72" v:groupContext="shape" transform="translate(6.85967,-22.443) rotate(-38.1076)">
+			<title>Sheet.72</title>
+			<path d="M-0 424.56 A96.1331 44.4001 55.03 0 1 68.24 420.56 L68.51 420.79" class="st17"/>
+		</g>
+		<g id="shape73-299" v:mID="73" v:groupContext="shape" transform="translate(328.501,-135.937)">
+			<title>Rectangle.73</title>
+			<desc>Key 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow73-300" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>		</g>
+		<g id="shape74-305" v:mID="74" v:groupContext="shape" transform="translate(362.251,-135.937)">
+			<title>Rectangle.74</title>
+			<desc>Action 1</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow74-306" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>		</g>
+		<g id="shape75-311" v:mID="75" v:groupContext="shape" transform="translate(409.501,-135.937)">
+			<title>Rectangle.75</title>
+			<desc>Key 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow75-312" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>		</g>
+		<g id="shape76-317" v:mID="76" v:groupContext="shape" transform="translate(443.251,-135.937)">
+			<title>Rectangle.76</title>
+			<desc>Action 2</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow76-318" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>		</g>
+		<g id="shape77-323" v:mID="77" v:groupContext="shape" transform="translate(490.501,-135.937)">
+			<title>Rectangle.77</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow77-324" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape78-328" v:mID="78" v:groupContext="shape" transform="translate(524.251,-135.937)">
+			<title>Rectangle.78</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow78-329" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape79-333" v:mID="79" v:groupContext="shape" transform="translate(328.501,-113.437)">
+			<title>Rectangle.79</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow79-334" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape80-338" v:mID="80" v:groupContext="shape" transform="translate(362.251,-113.437)">
+			<title>Rectangle.80</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow80-339" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape81-343" v:mID="81" v:groupContext="shape" transform="translate(409.501,-113.437)">
+			<title>Rectangle.81</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow81-344" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape82-348" v:mID="82" v:groupContext="shape" transform="translate(443.251,-113.437)">
+			<title>Rectangle.82</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow82-349" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape83-353" v:mID="83" v:groupContext="shape" transform="translate(490.501,-113.437)">
+			<title>Rectangle.83</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow83-354" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape84-358" v:mID="84" v:groupContext="shape" transform="translate(524.251,-113.437)">
+			<title>Rectangle.84</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow84-359" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape85-363" v:mID="85" v:groupContext="shape" transform="translate(328.501,-77.4375)">
+			<title>Rectangle.85</title>
+			<desc>Key x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow85-364" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>		</g>
+		<g id="shape86-369" v:mID="86" v:groupContext="shape" transform="translate(362.251,-77.4375)">
+			<title>Rectangle.86</title>
+			<desc>Action x</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow86-370" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>		</g>
+		<g id="shape87-375" v:mID="87" v:groupContext="shape" transform="translate(409.501,-77.4375)">
+			<title>Rectangle.87</title>
+			<desc>Key y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow87-376" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>		</g>
+		<g id="shape88-381" v:mID="88" v:groupContext="shape" transform="translate(443.251,-77.4375)">
+			<title>Rectangle.88</title>
+			<desc>Action y</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow88-382" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>		</g>
+		<g id="shape89-387" v:mID="89" v:groupContext="shape" transform="translate(490.501,-77.4375)">
+			<title>Rectangle.89</title>
+			<desc>Key z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow89-388" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>		</g>
+		<g id="shape90-393" v:mID="90" v:groupContext="shape" transform="translate(524.251,-77.4375)">
+			<title>Rectangle.90</title>
+			<desc>Action z</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow90-394" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>		</g>
+		<g id="shape91-399" v:mID="91" v:groupContext="shape" transform="translate(328.501,-40.9375)">
+			<title>Rectangle.91</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow91-400" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape92-404" v:mID="92" v:groupContext="shape" transform="translate(362.251,-40.9375)">
+			<title>Rectangle.92</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow92-405" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape93-409" v:mID="93" v:groupContext="shape" transform="translate(409.501,-40.9375)">
+			<title>Rectangle.93</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow93-410" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+		</g>
+		<g id="shape94-414" v:mID="94" v:groupContext="shape" transform="translate(443.251,-40.9375)">
+			<title>Rectangle.94</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow94-415" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+		</g>
+		<g id="shape95-419" v:mID="95" v:groupContext="shape" transform="translate(490.501,-40.9375)">
+			<title>Rectangle.95</title>
+			<desc>Key N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+			<g id="shadow95-420" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+			<text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>		</g>
+		<g id="shape96-425" v:mID="96" v:groupContext="shape" transform="translate(524.251,-40.9375)">
+			<title>Rectangle.96</title>
+			<desc>Action N</desc>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+			<g id="shadow96-426" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+			</g>
+			<rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+			<text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>		</g>
+		<g id="shape97-431" v:mID="97" v:groupContext="shape" transform="translate(326.251,-31.9375)">
+			<title>Rectangle.97</title>
+			<v:userDefs>
+				<v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+			</v:userDefs>
+			<g id="shadow97-432" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+				<rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+			</g>
+			<rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+		</g>
+		<g id="shape98-436" v:mID="98" v:groupContext="shape" transform="translate(337.501,-162.938)">
+			<title>Sheet.98</title>
+			<desc>Local Table for N Specific Flows Serviced at Node X</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+			<rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+			<text x="5.55" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node X</text>		</g>
+		<g id="shape99-439" v:mID="99" v:groupContext="shape" transform="translate(-204.342,-29.4449) rotate(-53.7462)">
+			<title>Sheet.99</title>
+			<path d="M0 424.56 L160.37 424.56" class="st25"/>
+		</g>
+		<g id="shape100-445" v:mID="100" v:groupContext="shape" transform="translate(-37.6568,-164.882) rotate(-24.444)">
+			<title>Sheet.100</title>
+			<path d="M0 424.56 L101.71 424.56" class="st25"/>
+		</g>
+		<g id="shape101-450" v:mID="101" v:groupContext="shape" transform="translate(464.049,-50.8578) rotate(50.099)">
+			<title>Sheet.101</title>
+			<path d="M0 424.56 L139.8 424.56" class="st25"/>
+		</g>
+		<g id="shape102-455" v:mID="102" v:groupContext="shape" transform="translate(372.376,-207.438)">
+			<title>Sheet.102</title>
+			<desc>Supports N Flows</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+			<rect x="0" y="406.565" width="135" height="18" class="st19"/>
+			<text x="25.15" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports N Flows</text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i7.svg b/doc/guides/prog_guide/img/efd_i7.svg
new file mode 100644
index 0000000..98f8000
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i7.svg
@@ -0,0 +1,790 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i8.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="10.6168in" height="4.81965in"
+		viewBox="0 0 764.409 347.015" xml:space="preserve" color-interpolation-filters="sRGB" class="st27">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {fill:#ffffff;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st2 {fill:none;stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st3 {stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st4 {fill:#000000;font-family:Intel Clear;font-size:0.998566em}
+		.st5 {fill:#0071c5;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st6 {stroke:#00b050;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st7 {stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st8 {stroke:#ca8f02;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st9 {stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st10 {fill:#c00000;font-family:Intel Clear;font-size:0.828804em;font-weight:bold}
+		.st11 {fill:#7f6d00;font-family:Intel Clear;font-size:0.828804em;font-weight:bold}
+		.st12 {fill:#00b050;stroke:#00b050;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0149927}
+		.st13 {fill:#004280;stroke:#004280;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0149927}
+		.st14 {fill:#00b050;stroke:#00b050;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0299855}
+		.st15 {fill:#ca8f02;stroke:#ca8f02;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.0299855}
+		.st16 {fill:#004280;font-family:Intel Clear;font-size:0.828804em}
+		.st17 {fill:#ffffff;font-family:Intel Clear;font-size:0.998566em}
+		.st18 {fill:#ffffff;font-family:Intel Clear;font-size:1.49785em}
+		.st19 {visibility:visible}
+		.st20 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+		.st21 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+		.st22 {fill:#feffff;font-family:Symbol;font-size:1.16666em}
+		.st23 {font-size:1em}
+		.st24 {font-family:Calibri;font-size:1em}
+		.st25 {fill:none;stroke:none;stroke-width:0.25}
+		.st26 {fill:#ffffff;font-family:Calibri;font-size:1.00001em}
+		.st27 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<defs id="Filters">
+		<filter id="filter_2">
+			<feGaussianBlur stdDeviation="2"/>
+		</filter>
+	</defs>
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(27.7836,-307.505)">
+			<title>Sheet.3</title>
+			<path d="M0 329.94 C-0 328.06 1.54 326.52 3.42 326.52 L68.49 326.52 C70.38 326.52 71.91 328.06 71.91 329.94 L71.91 343.6
+						 C71.91 345.49 70.38 347.02 68.49 347.02 L3.42 347.02 C1.54 347.02 0 345.49 0 343.6 L0 329.94 Z"
+					class="st1"/>
+		</g>
+		<g id="shape4-3" v:mID="4" v:groupContext="shape" transform="translate(27.7836,-307.505)">
+			<title>Sheet.4</title>
+			<path d="M0 329.94 C-0 328.06 1.54 326.52 3.42 326.52 L68.49 326.52 C70.38 326.52 71.91 328.06 71.91 329.94 L71.91 343.6
+						 C71.91 345.49 70.38 347.02 68.49 347.02 L3.42 347.02 C1.54 347.02 0 345.49 0 343.6 L0 329.94 Z"
+					class="st2"/>
+		</g>
+		<g id="shape5-5" v:mID="5" v:groupContext="shape" transform="translate(50.1544,-309.121)">
+			<title>Sheet.5</title>
+			<desc>Key1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.3237" cy="339.824" width="34.65" height="14.3829"/>
+			<path d="M34.65 332.63 L0 332.63 L0 347.02 L34.65 347.02 L34.65 332.63" class="st3"/>
+			<text x="3.72" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key1</text>		</g>
+		<g id="shape6-9" v:mID="6" v:groupContext="shape" transform="translate(43.6909,-286.954)">
+			<title>Sheet.6</title>
+			<path d="M0 336.65 L9.81 336.65 L9.81 326.28 L29.44 326.28 L29.44 336.65 L39.26 336.65 L19.63 347.02 L0 336.65 Z"
+					class="st5"/>
+		</g>
+		<g id="shape7-11" v:mID="7" v:groupContext="shape" transform="translate(27.7836,-266.044)">
+			<title>Sheet.7</title>
+			<path d="M0 330.04 C0 328.16 1.52 326.64 3.41 326.64 L68.51 326.64 C70.4 326.64 71.91 328.16 71.91 330.04 L71.91 343.62
+						 C71.91 345.49 70.4 347.02 68.51 347.02 L3.41 347.02 C1.52 347.02 0 345.49 0 343.62 L0 330.04 Z"
+					class="st1"/>
+		</g>
+		<g id="shape8-13" v:mID="8" v:groupContext="shape" transform="translate(27.7836,-266.044)">
+			<title>Sheet.8</title>
+			<path d="M0 330.04 C0 328.16 1.52 326.64 3.41 326.64 L68.51 326.64 C70.4 326.64 71.91 328.16 71.91 330.04 L71.91 343.62
+						 C71.91 345.49 70.4 347.02 68.51 347.02 L3.41 347.02 C1.52 347.02 0 345.49 0 343.62 L0 330.04 Z"
+					class="st2"/>
+		</g>
+		<g id="shape9-15" v:mID="9" v:groupContext="shape" transform="translate(50.7572,-267.602)">
+			<title>Sheet.9</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.5866" cy="339.824" width="33.18" height="14.3829"/>
+			<path d="M33.17 332.63 L0 332.63 L0 347.02 L33.17 347.02 L33.17 332.63" class="st3"/>
+			<text x="3.63" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape10-19" v:mID="10" v:groupContext="shape" transform="translate(19.0195,-225.183)">
+			<title>Sheet.10</title>
+			<path d="M0 330.74 C0 328.94 1.46 327.48 3.26 327.48 L87.15 327.48 C88.95 327.48 90.4 328.94 90.4 330.74 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.26 347.02 C1.46 347.02 0 345.56 0 343.76 L0 330.74 Z"
+					class="st1"/>
+		</g>
+		<g id="shape11-21" v:mID="11" v:groupContext="shape" transform="translate(19.0195,-225.183)">
+			<title>Sheet.11</title>
+			<path d="M0 330.74 C0 328.94 1.46 327.48 3.26 327.48 L87.15 327.48 C88.95 327.48 90.4 328.94 90.4 330.74 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.26 347.02 C1.46 347.02 0 345.56 0 343.76 L0 330.74 Z"
+					class="st2"/>
+		</g>
+		<g id="shape12-23" v:mID="12" v:groupContext="shape" transform="translate(28.0373,-226.287)">
+			<title>Sheet.12</title>
+			<desc>0x0102ABCD</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.3615" cy="339.824" width="86.73" height="14.3829"/>
+			<path d="M86.72 332.63 L0 332.63 L0 347.02 L86.72 347.02 L86.72 332.63" class="st3"/>
+			<text x="7.12" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0102ABCD</text>		</g>
+		<g id="shape13-27" v:mID="13" v:groupContext="shape" transform="translate(43.6909,-244.775)">
+			<title>Sheet.13</title>
+			<path d="M0 336.71 L9.81 336.71 L9.81 326.4 L29.44 326.4 L29.44 336.71 L39.26 336.71 L19.63 347.02 L0 336.71 Z"
+					class="st5"/>
+		</g>
+		<g id="shape14-29" v:mID="14" v:groupContext="shape" transform="translate(40.7496,-210.444)">
+			<title>Sheet.14</title>
+			<path d="M26.29 334.91 C26.29 338.26 25.84 340.96 25.29 340.96 L14.16 340.96 C13.6 340.96 13.15 343.67 13.15 347.02 C13.15
+						 343.67 12.7 340.96 12.14 340.96 L1.01 340.96 C0.46 340.96 0 338.26 0 334.91" class="st6"/>
+		</g>
+		<g id="shape15-32" v:mID="15" v:groupContext="shape" transform="translate(125.629,-307.625)">
+			<title>Sheet.15</title>
+			<path d="M0 330.04 C0 328.16 1.52 326.64 3.41 326.64 L68.63 326.64 C70.51 326.64 72.03 328.16 72.03 330.04 L72.03 343.62
+						 C72.03 345.49 70.51 347.02 68.63 347.02 L3.41 347.02 C1.52 347.02 0 345.49 0 343.62 L0 330.04 Z"
+					class="st1"/>
+		</g>
+		<g id="shape16-34" v:mID="16" v:groupContext="shape" transform="translate(125.629,-307.625)">
+			<title>Sheet.16</title>
+			<path d="M0 330.04 C0 328.16 1.52 326.64 3.41 326.64 L68.63 326.64 C70.51 326.64 72.03 328.16 72.03 330.04 L72.03 343.62
+						 C72.03 345.49 70.51 347.02 68.63 347.02 L3.41 347.02 C1.52 347.02 0 345.49 0 343.62 L0 330.04 Z"
+					class="st2"/>
+		</g>
+		<g id="shape17-36" v:mID="17" v:groupContext="shape" transform="translate(148.034,-309.155)">
+			<title>Sheet.17</title>
+			<desc>Key2</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.3237" cy="339.824" width="34.65" height="14.3829"/>
+			<path d="M34.65 332.63 L0 332.63 L0 347.02 L34.65 347.02 L34.65 332.63" class="st3"/>
+			<text x="3.72" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key2</text>		</g>
+		<g id="shape18-40" v:mID="18" v:groupContext="shape" transform="translate(141.536,-286.954)">
+			<title>Sheet.18</title>
+			<path d="M0 336.65 L9.81 336.65 L9.81 326.28 L29.44 326.28 L29.44 336.65 L39.26 336.65 L19.63 347.02 L0 336.65 Z"
+					class="st5"/>
+		</g>
+		<g id="shape19-42" v:mID="19" v:groupContext="shape" transform="translate(125.629,-266.044)">
+			<title>Sheet.19</title>
+			<path d="M0 329.94 C0 328.06 1.54 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.06 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.54 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st1"/>
+		</g>
+		<g id="shape20-44" v:mID="20" v:groupContext="shape" transform="translate(125.629,-266.044)">
+			<title>Sheet.20</title>
+			<path d="M0 329.94 C0 328.06 1.54 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.06 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.54 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st2"/>
+		</g>
+		<g id="shape21-46" v:mID="21" v:groupContext="shape" transform="translate(148.636,-267.636)">
+			<title>Sheet.21</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.5866" cy="339.824" width="33.18" height="14.3829"/>
+			<path d="M33.17 332.63 L0 332.63 L0 347.02 L33.17 347.02 L33.17 332.63" class="st3"/>
+			<text x="3.63" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape22-50" v:mID="22" v:groupContext="shape" transform="translate(116.865,-225.183)">
+			<title>Sheet.22</title>
+			<path d="M0 330.74 C0 328.94 1.46 327.48 3.26 327.48 L87.15 327.48 C88.95 327.48 90.4 328.94 90.4 330.74 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.26 347.02 C1.46 347.02 0 345.56 0 343.76 L0 330.74 Z"
+					class="st1"/>
+		</g>
+		<g id="shape23-52" v:mID="23" v:groupContext="shape" transform="translate(116.865,-225.183)">
+			<title>Sheet.23</title>
+			<path d="M0 330.74 C0 328.94 1.46 327.48 3.26 327.48 L87.15 327.48 C88.95 327.48 90.4 328.94 90.4 330.74 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.26 347.02 C1.46 347.02 0 345.56 0 343.76 L0 330.74 Z"
+					class="st2"/>
+		</g>
+		<g id="shape24-54" v:mID="24" v:groupContext="shape" transform="translate(125.917,-226.322)">
+			<title>Sheet.24</title>
+			<desc>0x0103CDAB</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.3615" cy="339.824" width="86.73" height="14.3829"/>
+			<path d="M86.72 332.63 L0 332.63 L0 347.02 L86.72 347.02 L86.72 332.63" class="st3"/>
+			<text x="7.12" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0103CDAB</text>		</g>
+		<g id="shape25-58" v:mID="25" v:groupContext="shape" transform="translate(141.536,-244.775)">
+			<title>Sheet.25</title>
+			<path d="M0 336.71 L9.81 336.71 L9.81 326.4 L29.44 326.4 L29.44 336.71 L39.26 336.71 L19.63 347.02 L0 336.71 Z"
+					class="st5"/>
+		</g>
+		<g id="shape26-60" v:mID="26" v:groupContext="shape" transform="translate(138.595,-210.444)">
+			<title>Sheet.26</title>
+			<path d="M26.29 334.91 C26.29 338.26 25.84 340.96 25.29 340.96 L14.16 340.96 C13.6 340.96 13.15 343.67 13.15 347.02 C13.15
+						 343.67 12.7 340.96 12.14 340.96 L1.01 340.96 C0.46 340.96 0 338.26 0 334.91" class="st7"/>
+		</g>
+		<g id="shape27-63" v:mID="27" v:groupContext="shape" transform="translate(221.793,-307.625)">
+			<title>Sheet.27</title>
+			<path d="M0 330.04 C0 328.17 1.53 326.64 3.41 326.64 L68.64 326.64 C70.52 326.64 72.03 328.17 72.03 330.04 L72.03 343.63
+						 C72.03 345.5 70.52 347.02 68.64 347.02 L3.41 347.02 C1.53 347.02 0 345.5 0 343.63 L0 330.04 Z" class="st1"/>
+		</g>
+		<g id="shape28-65" v:mID="28" v:groupContext="shape" transform="translate(221.793,-307.625)">
+			<title>Sheet.28</title>
+			<path d="M0 330.04 C0 328.17 1.53 326.64 3.41 326.64 L68.64 326.64 C70.52 326.64 72.03 328.17 72.03 330.04 L72.03 343.63
+						 C72.03 345.5 70.52 347.02 68.64 347.02 L3.41 347.02 C1.53 347.02 0 345.5 0 343.63 L0 330.04 Z" class="st2"/>
+		</g>
+		<g id="shape29-67" v:mID="29" v:groupContext="shape" transform="translate(244.237,-309.155)">
+			<title>Sheet.29</title>
+			<desc>Key3</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.3237" cy="339.824" width="34.65" height="14.3829"/>
+			<path d="M34.65 332.63 L0 332.63 L0 347.02 L34.65 347.02 L34.65 332.63" class="st3"/>
+			<text x="3.72" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key3</text>		</g>
+		<g id="shape30-71" v:mID="30" v:groupContext="shape" transform="translate(237.701,-286.954)">
+			<title>Sheet.30</title>
+			<path d="M0 336.65 L9.84 336.65 L9.84 326.28 L29.53 326.28 L29.53 336.65 L39.38 336.65 L19.69 347.02 L0 336.65 Z"
+					class="st5"/>
+		</g>
+		<g id="shape31-73" v:mID="31" v:groupContext="shape" transform="translate(221.793,-266.044)">
+			<title>Sheet.31</title>
+			<path d="M0 329.94 C-0 328.07 1.55 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.07 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.55 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st1"/>
+		</g>
+		<g id="shape32-75" v:mID="32" v:groupContext="shape" transform="translate(221.793,-266.044)">
+			<title>Sheet.32</title>
+			<path d="M0 329.94 C-0 328.07 1.55 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.07 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.55 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st2"/>
+		</g>
+		<g id="shape33-77" v:mID="33" v:groupContext="shape" transform="translate(244.84,-267.636)">
+			<title>Sheet.33</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.5866" cy="339.824" width="33.18" height="14.3829"/>
+			<path d="M33.17 332.63 L0 332.63 L0 347.02 L33.17 347.02 L33.17 332.63" class="st3"/>
+			<text x="3.63" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape34-81" v:mID="34" v:groupContext="shape" transform="translate(213.029,-225.183)">
+			<title>Sheet.34</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.27 327.48 C89.07 327.48 90.52 328.95 90.52 330.75 L90.52 343.76
+						 C90.52 345.56 89.07 347.02 87.27 347.02 L3.27 347.02 C1.47 347.02 0 345.56 0 343.76 L0 330.75 Z"
+					class="st1"/>
+		</g>
+		<g id="shape35-83" v:mID="35" v:groupContext="shape" transform="translate(213.029,-225.183)">
+			<title>Sheet.35</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.27 327.48 C89.07 327.48 90.52 328.95 90.52 330.75 L90.52 343.76
+						 C90.52 345.56 89.07 347.02 87.27 347.02 L3.27 347.02 C1.47 347.02 0 345.56 0 343.76 L0 330.75 Z"
+					class="st2"/>
+		</g>
+		<g id="shape36-85" v:mID="36" v:groupContext="shape" transform="translate(222.002,-226.322)">
+			<title>Sheet.36</title>
+			<desc>0x0102BAAD</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.4787" cy="339.824" width="86.96" height="14.3829"/>
+			<path d="M86.96 332.63 L0 332.63 L0 347.02 L86.96 347.02 L86.96 332.63" class="st3"/>
+			<text x="7.13" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0102BAAD</text>		</g>
+		<g id="shape37-89" v:mID="37" v:groupContext="shape" transform="translate(237.701,-244.775)">
+			<title>Sheet.37</title>
+			<path d="M0 336.71 L9.84 336.71 L9.84 326.4 L29.53 326.4 L29.53 336.71 L39.38 336.71 L19.69 347.02 L0 336.71 Z"
+					class="st5"/>
+		</g>
+		<g id="shape38-91" v:mID="38" v:groupContext="shape" transform="translate(234.759,-210.444)">
+			<title>Sheet.38</title>
+			<path d="M26.41 334.91 C26.41 338.26 25.96 340.96 25.41 340.96 L14.22 340.96 C13.66 340.96 13.21 343.67 13.21 347.02
+						 C13.21 343.67 12.76 340.96 12.2 340.96 L1.01 340.96 C0.46 340.96 0 338.26 0 334.91" class="st6"/>
+		</g>
+		<g id="shape39-94" v:mID="39" v:groupContext="shape" transform="translate(319.759,-307.625)">
+			<title>Sheet.39</title>
+			<path d="M0 330.04 C0 328.17 1.53 326.64 3.41 326.64 L68.52 326.64 C70.4 326.64 71.91 328.17 71.91 330.04 L71.91 343.63
+						 C71.91 345.5 70.4 347.02 68.52 347.02 L3.41 347.02 C1.53 347.02 0 345.5 0 343.63 L0 330.04 Z" class="st1"/>
+		</g>
+		<g id="shape40-96" v:mID="40" v:groupContext="shape" transform="translate(319.759,-307.625)">
+			<title>Sheet.40</title>
+			<path d="M0 330.04 C0 328.17 1.53 326.64 3.41 326.64 L68.52 326.64 C70.4 326.64 71.91 328.17 71.91 330.04 L71.91 343.63
+						 C71.91 345.5 70.4 347.02 68.52 347.02 L3.41 347.02 C1.53 347.02 0 345.5 0 343.63 L0 330.04 Z" class="st2"/>
+		</g>
+		<g id="shape41-98" v:mID="41" v:groupContext="shape" transform="translate(342.125,-309.155)">
+			<title>Sheet.41</title>
+			<desc>Key4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.3237" cy="339.824" width="34.65" height="14.3829"/>
+			<path d="M34.65 332.63 L0 332.63 L0 347.02 L34.65 347.02 L34.65 332.63" class="st3"/>
+			<text x="3.72" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key4</text>		</g>
+		<g id="shape42-102" v:mID="42" v:groupContext="shape" transform="translate(335.666,-286.954)">
+			<title>Sheet.42</title>
+			<path d="M0 336.65 L9.81 336.65 L9.81 326.28 L29.44 326.28 L29.44 336.65 L39.26 336.65 L19.63 347.02 L0 336.65 Z"
+					class="st5"/>
+		</g>
+		<g id="shape43-104" v:mID="43" v:groupContext="shape" transform="translate(319.759,-266.044)">
+			<title>Sheet.43</title>
+			<path d="M0 329.94 C0 328.07 1.55 326.52 3.42 326.52 L68.49 326.52 C70.38 326.52 71.91 328.07 71.91 329.94 L71.91 343.6
+						 C71.91 345.49 70.38 347.02 68.49 347.02 L3.42 347.02 C1.55 347.02 0 345.49 0 343.6 L0 329.94 Z"
+					class="st1"/>
+		</g>
+		<g id="shape44-106" v:mID="44" v:groupContext="shape" transform="translate(319.759,-266.044)">
+			<title>Sheet.44</title>
+			<path d="M0 329.94 C0 328.07 1.55 326.52 3.42 326.52 L68.49 326.52 C70.38 326.52 71.91 328.07 71.91 329.94 L71.91 343.6
+						 C71.91 345.49 70.38 347.02 68.49 347.02 L3.42 347.02 C1.55 347.02 0 345.49 0 343.6 L0 329.94 Z"
+					class="st2"/>
+		</g>
+		<g id="shape45-108" v:mID="45" v:groupContext="shape" transform="translate(342.728,-267.636)">
+			<title>Sheet.45</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.5866" cy="339.824" width="33.18" height="14.3829"/>
+			<path d="M33.17 332.63 L0 332.63 L0 347.02 L33.17 347.02 L33.17 332.63" class="st3"/>
+			<text x="3.63" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape46-112" v:mID="46" v:groupContext="shape" transform="translate(310.995,-225.183)">
+			<title>Sheet.46</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.15 327.48 C88.95 327.48 90.4 328.95 90.4 330.75 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.27 347.02 C1.47 347.02 0 345.56 0 343.76 L0 330.75 Z"
+					class="st1"/>
+		</g>
+		<g id="shape47-114" v:mID="47" v:groupContext="shape" transform="translate(310.995,-225.183)">
+			<title>Sheet.47</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.15 327.48 C88.95 327.48 90.4 328.95 90.4 330.75 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.27 347.02 C1.47 347.02 0 345.56 0 343.76 L0 330.75 Z"
+					class="st2"/>
+		</g>
+		<g id="shape48-116" v:mID="48" v:groupContext="shape" transform="translate(321.689,-226.322)">
+			<title>Sheet.48</title>
+			<desc>0x0104BEEF</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="41.4183" cy="339.824" width="82.84" height="14.3829"/>
+			<path d="M82.84 332.63 L0 332.63 L0 347.02 L82.84 347.02 L82.84 332.63" class="st3"/>
+			<text x="6.87" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0104BEEF</text>		</g>
+		<g id="shape49-120" v:mID="49" v:groupContext="shape" transform="translate(335.666,-244.775)">
+			<title>Sheet.49</title>
+			<path d="M0 336.71 L9.81 336.71 L9.81 326.4 L29.44 326.4 L29.44 336.71 L39.26 336.71 L19.63 347.02 L0 336.71 Z"
+					class="st5"/>
+		</g>
+		<g id="shape50-122" v:mID="50" v:groupContext="shape" transform="translate(332.725,-210.444)">
+			<title>Sheet.50</title>
+			<path d="M26.29 334.91 C26.29 338.27 25.84 340.96 25.29 340.96 L14.17 340.96 C13.61 340.96 13.15 343.67 13.15 347.02
+						 C13.15 343.67 12.7 340.96 12.14 340.96 L1.02 340.96 C0.47 340.96 0 338.27 0 334.91" class="st6"/>
+		</g>
+		<g id="shape51-125" v:mID="51" v:groupContext="shape" transform="translate(416.884,-307.625)">
+			<title>Sheet.51</title>
+			<path d="M0 330.04 C0 328.17 1.53 326.64 3.41 326.64 L68.52 326.64 C70.4 326.64 71.91 328.17 71.91 330.04 L71.91 343.63
+						 C71.91 345.5 70.4 347.02 68.52 347.02 L3.41 347.02 C1.53 347.02 0 345.5 0 343.63 L0 330.04 Z" class="st1"/>
+		</g>
+		<g id="shape52-127" v:mID="52" v:groupContext="shape" transform="translate(416.884,-307.625)">
+			<title>Sheet.52</title>
+			<path d="M0 330.04 C0 328.17 1.53 326.64 3.41 326.64 L68.52 326.64 C70.4 326.64 71.91 328.17 71.91 330.04 L71.91 343.63
+						 C71.91 345.5 70.4 347.02 68.52 347.02 L3.41 347.02 C1.53 347.02 0 345.5 0 343.63 L0 330.04 Z" class="st2"/>
+		</g>
+		<g id="shape53-129" v:mID="53" v:groupContext="shape" transform="translate(439.255,-309.155)">
+			<title>Sheet.53</title>
+			<desc>Key5</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.3237" cy="339.824" width="34.65" height="14.3829"/>
+			<path d="M34.65 332.63 L0 332.63 L0 347.02 L34.65 347.02 L34.65 332.63" class="st3"/>
+			<text x="3.72" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key5</text>		</g>
+		<g id="shape54-133" v:mID="54" v:groupContext="shape" transform="translate(432.791,-286.954)">
+			<title>Sheet.54</title>
+			<path d="M0 336.65 L9.81 336.65 L9.81 326.28 L29.44 326.28 L29.44 336.65 L39.26 336.65 L19.63 347.02 L0 336.65 Z"
+					class="st5"/>
+		</g>
+		<g id="shape55-135" v:mID="55" v:groupContext="shape" transform="translate(416.884,-266.044)">
+			<title>Sheet.55</title>
+			<path d="M0 329.94 C0 328.07 1.55 326.52 3.42 326.52 L68.49 326.52 C70.38 326.52 71.91 328.07 71.91 329.94 L71.91 343.6
+						 C71.91 345.49 70.38 347.02 68.49 347.02 L3.42 347.02 C1.55 347.02 0 345.49 0 343.6 L0 329.94 Z"
+					class="st1"/>
+		</g>
+		<g id="shape56-137" v:mID="56" v:groupContext="shape" transform="translate(416.884,-266.044)">
+			<title>Sheet.56</title>
+			<path d="M0 329.94 C0 328.07 1.55 326.52 3.42 326.52 L68.49 326.52 C70.38 326.52 71.91 328.07 71.91 329.94 L71.91 343.6
+						 C71.91 345.49 70.38 347.02 68.49 347.02 L3.42 347.02 C1.55 347.02 0 345.49 0 343.6 L0 329.94 Z"
+					class="st2"/>
+		</g>
+		<g id="shape57-139" v:mID="57" v:groupContext="shape" transform="translate(439.858,-267.636)">
+			<title>Sheet.57</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.5866" cy="339.824" width="33.18" height="14.3829"/>
+			<path d="M33.17 332.63 L0 332.63 L0 347.02 L33.17 347.02 L33.17 332.63" class="st3"/>
+			<text x="3.63" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape58-143" v:mID="58" v:groupContext="shape" transform="translate(408.12,-225.183)">
+			<title>Sheet.58</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.15 327.48 C88.95 327.48 90.4 328.95 90.4 330.75 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.27 347.02 C1.47 347.02 0 345.56 0 343.76 L0 330.75 Z"
+					class="st1"/>
+		</g>
+		<g id="shape59-145" v:mID="59" v:groupContext="shape" transform="translate(408.12,-225.183)">
+			<title>Sheet.59</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.15 327.48 C88.95 327.48 90.4 328.95 90.4 330.75 L90.4 343.76
+						 C90.4 345.56 88.95 347.02 87.15 347.02 L3.27 347.02 C1.47 347.02 0 345.56 0 343.76 L0 330.75 Z"
+					class="st2"/>
+		</g>
+		<g id="shape60-147" v:mID="60" v:groupContext="shape" transform="translate(416.778,-226.322)">
+			<title>Sheet.60</title>
+			<desc>0x0103DABD</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.7817" cy="339.824" width="87.57" height="14.3829"/>
+			<path d="M87.56 332.63 L0 332.63 L0 347.02 L87.56 347.02 L87.56 332.63" class="st3"/>
+			<text x="7.17" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0103DABD</text>		</g>
+		<g id="shape61-151" v:mID="61" v:groupContext="shape" transform="translate(432.791,-244.775)">
+			<title>Sheet.61</title>
+			<path d="M0 336.71 L9.81 336.71 L9.81 326.4 L29.44 326.4 L29.44 336.71 L39.26 336.71 L19.63 347.02 L0 336.71 Z"
+					class="st5"/>
+		</g>
+		<g id="shape62-153" v:mID="62" v:groupContext="shape" transform="translate(429.85,-210.444)">
+			<title>Sheet.62</title>
+			<path d="M26.29 334.91 C26.29 338.27 25.84 340.96 25.29 340.96 L14.17 340.96 C13.61 340.96 13.15 343.67 13.15 347.02
+						 C13.15 343.67 12.7 340.96 12.14 340.96 L1.02 340.96 C0.47 340.96 0 338.27 0 334.91" class="st7"/>
+		</g>
+		<g id="shape63-156" v:mID="63" v:groupContext="shape" transform="translate(514.489,-307.625)">
+			<title>Sheet.63</title>
+			<path d="M0 330.06 C-0 328.17 1.53 326.64 3.42 326.64 L68.64 326.64 C70.53 326.64 72.03 328.17 72.03 330.06 L72.03 343.63
+						 C72.03 345.52 70.53 347.02 68.64 347.02 L3.42 347.02 C1.53 347.02 0 345.52 0 343.63 L0 330.06 Z"
+					class="st1"/>
+		</g>
+		<g id="shape64-158" v:mID="64" v:groupContext="shape" transform="translate(514.489,-307.625)">
+			<title>Sheet.64</title>
+			<path d="M0 330.06 C-0 328.17 1.53 326.64 3.42 326.64 L68.64 326.64 C70.53 326.64 72.03 328.17 72.03 330.06 L72.03 343.63
+						 C72.03 345.52 70.53 347.02 68.64 347.02 L3.42 347.02 C1.53 347.02 0 345.52 0 343.63 L0 330.06 Z"
+					class="st2"/>
+		</g>
+		<g id="shape65-160" v:mID="65" v:groupContext="shape" transform="translate(536.883,-309.19)">
+			<title>Sheet.65</title>
+			<desc>Key6</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.3237" cy="339.824" width="34.65" height="14.3829"/>
+			<path d="M34.65 332.63 L0 332.63 L0 347.02 L34.65 347.02 L34.65 332.63" class="st3"/>
+			<text x="3.72" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key6</text>		</g>
+		<g id="shape66-164" v:mID="66" v:groupContext="shape" transform="translate(530.396,-287.074)">
+			<title>Sheet.66</title>
+			<path d="M0 336.71 L9.81 336.71 L9.81 326.4 L29.44 326.4 L29.44 336.71 L39.26 336.71 L19.63 347.02 L0 336.71 Z"
+					class="st5"/>
+		</g>
+		<g id="shape67-166" v:mID="67" v:groupContext="shape" transform="translate(514.489,-266.044)">
+			<title>Sheet.67</title>
+			<path d="M0 329.94 C0 328.08 1.56 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.08 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.56 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st1"/>
+		</g>
+		<g id="shape68-168" v:mID="68" v:groupContext="shape" transform="translate(514.489,-266.044)">
+			<title>Sheet.68</title>
+			<path d="M0 329.94 C0 328.08 1.56 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.08 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.56 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st2"/>
+		</g>
+		<g id="shape69-170" v:mID="69" v:groupContext="shape" transform="translate(537.486,-267.671)">
+			<title>Sheet.69</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.5866" cy="339.824" width="33.18" height="14.3829"/>
+			<path d="M33.17 332.63 L0 332.63 L0 347.02 L33.17 347.02 L33.17 332.63" class="st3"/>
+			<text x="3.63" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape70-174" v:mID="70" v:groupContext="shape" transform="translate(505.725,-225.183)">
+			<title>Sheet.70</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.16 327.48 C88.96 327.48 90.4 328.95 90.4 330.75 L90.4 343.78
+						 C90.4 345.58 88.96 347.02 87.16 347.02 L3.27 347.02 C1.47 347.02 0 345.58 0 343.78 L0 330.75 Z"
+					class="st1"/>
+		</g>
+		<g id="shape71-176" v:mID="71" v:groupContext="shape" transform="translate(505.725,-225.183)">
+			<title>Sheet.71</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.16 327.48 C88.96 327.48 90.4 328.95 90.4 330.75 L90.4 343.78
+						 C90.4 345.58 88.96 347.02 87.16 347.02 L3.27 347.02 C1.47 347.02 0 345.58 0 343.78 L0 330.75 Z"
+					class="st2"/>
+		</g>
+		<g id="shape72-178" v:mID="72" v:groupContext="shape" transform="translate(514.766,-226.356)">
+			<title>Sheet.72</title>
+			<desc>0x0102ADCB</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.3615" cy="339.824" width="86.73" height="14.3829"/>
+			<path d="M86.72 332.63 L0 332.63 L0 347.02 L86.72 347.02 L86.72 332.63" class="st3"/>
+			<text x="7.12" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0102ADCB</text>		</g>
+		<g id="shape73-182" v:mID="73" v:groupContext="shape" transform="translate(530.396,-244.775)">
+			<title>Sheet.73</title>
+			<path d="M0 336.65 L9.81 336.65 L9.81 326.28 L29.44 326.28 L29.44 336.65 L39.26 336.65 L19.63 347.02 L0 336.65 Z"
+					class="st5"/>
+		</g>
+		<g id="shape74-184" v:mID="74" v:groupContext="shape" transform="translate(527.455,-210.564)">
+			<title>Sheet.74</title>
+			<path d="M26.29 335.03 C26.29 338.36 25.87 341.02 25.3 341.02 L14.17 341.02 C13.6 341.02 13.15 343.72 13.15 347.02 C13.15
+						 343.72 12.73 341.02 12.16 341.02 L1.02 341.02 C0.45 341.02 0 338.36 0 335.03" class="st6"/>
+		</g>
+		<g id="shape75-187" v:mID="75" v:groupContext="shape" transform="translate(610.653,-307.505)">
+			<title>Sheet.75</title>
+			<path d="M0 329.94 C0 328.08 1.56 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.08 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.56 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st1"/>
+		</g>
+		<g id="shape76-189" v:mID="76" v:groupContext="shape" transform="translate(610.653,-307.505)">
+			<title>Sheet.76</title>
+			<path d="M0 329.94 C0 328.08 1.56 326.52 3.42 326.52 L68.61 326.52 C70.5 326.52 72.03 328.08 72.03 329.94 L72.03 343.6
+						 C72.03 345.49 70.5 347.02 68.61 347.02 L3.42 347.02 C1.56 347.02 0 345.49 0 343.6 L0 329.94 Z" class="st2"/>
+		</g>
+		<g id="shape77-191" v:mID="77" v:groupContext="shape" transform="translate(633.086,-309.121)">
+			<title>Sheet.77</title>
+			<desc>Key7</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="17.3237" cy="339.824" width="34.65" height="14.3829"/>
+			<path d="M34.65 332.63 L0 332.63 L0 347.02 L34.65 347.02 L34.65 332.63" class="st3"/>
+			<text x="3.72" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key7</text>		</g>
+		<g id="shape78-195" v:mID="78" v:groupContext="shape" transform="translate(626.561,-286.954)">
+			<title>Sheet.78</title>
+			<path d="M0 336.65 L9.84 336.65 L9.84 326.28 L29.53 326.28 L29.53 336.65 L39.38 336.65 L19.69 347.02 L0 336.65 Z"
+					class="st5"/>
+		</g>
+		<g id="shape79-197" v:mID="79" v:groupContext="shape" transform="translate(610.653,-266.044)">
+			<title>Sheet.79</title>
+			<path d="M0 330.06 C-0 328.17 1.53 326.64 3.42 326.64 L68.64 326.64 C70.53 326.64 72.03 328.17 72.03 330.06 L72.03 343.63
+						 C72.03 345.52 70.53 347.02 68.64 347.02 L3.42 347.02 C1.53 347.02 0 345.52 0 343.63 L0 330.06 Z"
+					class="st1"/>
+		</g>
+		<g id="shape80-199" v:mID="80" v:groupContext="shape" transform="translate(610.653,-266.044)">
+			<title>Sheet.80</title>
+			<path d="M0 330.06 C-0 328.17 1.53 326.64 3.42 326.64 L68.64 326.64 C70.53 326.64 72.03 328.17 72.03 330.06 L72.03 343.63
+						 C72.03 345.52 70.53 347.02 68.64 347.02 L3.42 347.02 C1.53 347.02 0 345.52 0 343.63 L0 330.06 Z"
+					class="st2"/>
+		</g>
+		<g id="shape81-201" v:mID="81" v:groupContext="shape" transform="translate(633.689,-267.602)">
+			<title>Sheet.81</title>
+			<desc>hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="16.5866" cy="339.824" width="33.18" height="14.3829"/>
+			<path d="M33.17 332.63 L0 332.63 L0 347.02 L33.17 347.02 L33.17 332.63" class="st3"/>
+			<text x="3.63" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash</text>		</g>
+		<g id="shape82-205" v:mID="82" v:groupContext="shape" transform="translate(601.889,-225.183)">
+			<title>Sheet.82</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.28 327.48 C89.08 327.48 90.52 328.95 90.52 330.75 L90.52 343.78
+						 C90.52 345.58 89.08 347.02 87.28 347.02 L3.27 347.02 C1.47 347.02 0 345.58 0 343.78 L0 330.75 Z"
+					class="st1"/>
+		</g>
+		<g id="shape83-207" v:mID="83" v:groupContext="shape" transform="translate(601.889,-225.183)">
+			<title>Sheet.83</title>
+			<path d="M0 330.75 C0 328.95 1.47 327.48 3.27 327.48 L87.28 327.48 C89.08 327.48 90.52 328.95 90.52 330.75 L90.52 343.78
+						 C90.52 345.58 89.08 347.02 87.28 347.02 L3.27 347.02 C1.47 347.02 0 345.58 0 343.78 L0 330.75 Z"
+					class="st2"/>
+		</g>
+		<g id="shape84-209" v:mID="84" v:groupContext="shape" transform="translate(610.969,-226.287)">
+			<title>Sheet.84</title>
+			<desc>0x0104DBCA</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="43.3615" cy="339.824" width="86.73" height="14.3829"/>
+			<path d="M86.72 332.63 L0 332.63 L0 347.02 L86.72 347.02 L86.72 332.63" class="st3"/>
+			<text x="7.12" y="343.42" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0104DBCA</text>		</g>
+		<g id="shape85-213" v:mID="85" v:groupContext="shape" transform="translate(626.561,-244.775)">
+			<title>Sheet.85</title>
+			<path d="M0 336.71 L9.84 336.71 L9.84 326.4 L29.53 326.4 L29.53 336.71 L39.38 336.71 L19.69 347.02 L0 336.71 Z"
+					class="st5"/>
+		</g>
+		<g id="shape86-215" v:mID="86" v:groupContext="shape" transform="translate(623.619,-210.444)">
+			<title>Sheet.86</title>
+			<path d="M26.41 334.91 C26.41 338.27 25.96 340.96 25.42 340.96 L14.23 340.96 C13.69 340.96 13.21 343.69 13.21 347.02
+						 C13.21 343.69 12.76 340.96 12.22 340.96 L1.02 340.96 C0.48 340.96 0 338.27 0 334.91" class="st8"/>
+		</g>
+		<g id="shape87-218" v:mID="87" v:groupContext="shape" transform="translate(242.323,-81.6288)">
+			<title>Sheet.87</title>
+			<path d="M0 281.23 L0 347.02 L41.18 347.02 L41.18 281.23 L0 281.23 L0 281.23 Z" class="st1"/>
+		</g>
+		<g id="shape88-220" v:mID="88" v:groupContext="shape" transform="translate(247.009,-81.6288)">
+			<title>Sheet.88</title>
+			<path d="M0 281.23 L41.18 281.23 L41.18 347.02 L0 347.02 L0 281.23" class="st9"/>
+		</g>
+		<g id="shape89-223" v:mID="89" v:groupContext="shape" transform="translate(245.254,-132.398)">
+			<title>Sheet.89</title>
+			<desc>0x0102</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="21.3211" cy="341.046" width="42.65" height="11.9384"/>
+			<path d="M42.64 335.08 L0 335.08 L0 347.02 L42.64 347.02 L42.64 335.08" class="st3"/>
+			<text x="4" y="344.03" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0102</text>		</g>
+		<g id="shape90-227" v:mID="90" v:groupContext="shape" transform="translate(245.015,-82.7016)">
+			<title>Sheet.90</title>
+			<desc>4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.79425" cy="341.046" width="9.59" height="11.9384"/>
+			<path d="M9.59 335.08 L0 335.08 L0 347.02 L9.59 347.02 L9.59 335.08" class="st3"/>
+			<text x="1.84" y="344.03" class="st11" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>4</text>		</g>
+		<g id="shape91-231" v:mID="91" v:groupContext="shape" transform="translate(336.326,-81.6288)">
+			<title>Sheet.91</title>
+			<path d="M0 281.23 L0 347.02 L41.18 347.02 L41.18 281.23 L0 281.23 L0 281.23 Z" class="st1"/>
+		</g>
+		<g id="shape92-233" v:mID="92" v:groupContext="shape" transform="translate(339.598,-81.6288)">
+			<title>Sheet.92</title>
+			<path d="M0 281.23 L41.18 281.23 L41.18 347.02 L0 347.02 L0 281.23" class="st9"/>
+		</g>
+		<g id="shape93-236" v:mID="93" v:groupContext="shape" transform="translate(339.264,-132.398)">
+			<title>Sheet.93</title>
+			<desc>0x0103</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="21.3211" cy="341.046" width="42.65" height="11.9384"/>
+			<path d="M42.64 335.08 L0 335.08 L0 347.02 L42.64 347.02 L42.64 335.08" class="st3"/>
+			<text x="4" y="344.03" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0103</text>		</g>
+		<g id="shape94-240" v:mID="94" v:groupContext="shape" transform="translate(339.024,-82.7016)">
+			<title>Sheet.94</title>
+			<desc>2</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.79425" cy="341.046" width="9.59" height="11.9384"/>
+			<path d="M9.59 335.08 L0 335.08 L0 347.02 L9.59 347.02 L9.59 335.08" class="st3"/>
+			<text x="1.84" y="344.03" class="st11" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>2</text>		</g>
+		<g id="shape95-244" v:mID="95" v:groupContext="shape" transform="translate(438.598,-81.5089)">
+			<title>Sheet.95</title>
+			<path d="M0 281.23 L0 347.02 L41.18 347.02 L41.18 281.23 L0 281.23 L0 281.23 Z" class="st1"/>
+		</g>
+		<g id="shape96-246" v:mID="96" v:groupContext="shape" transform="translate(438.598,-81.5089)">
+			<title>Sheet.96</title>
+			<path d="M0 281.23 L41.18 281.23 L41.18 347.02 L0 347.02 L0 281.23" class="st9"/>
+		</g>
+		<g id="shape97-249" v:mID="97" v:groupContext="shape" transform="translate(437.81,-132.27)">
+			<title>Sheet.97</title>
+			<desc>0x0104</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="21.3211" cy="341.046" width="42.65" height="11.9384"/>
+			<path d="M42.64 335.08 L0 335.08 L0 347.02 L42.64 347.02 L42.64 335.08" class="st3"/>
+			<text x="4" y="344.03" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>0x0104</text>		</g>
+		<g id="shape98-253" v:mID="98" v:groupContext="shape" transform="translate(437.57,-82.5735)">
+			<title>Sheet.98</title>
+			<desc>1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="4.79425" cy="341.046" width="9.59" height="11.9384"/>
+			<path d="M9.59 335.08 L0 335.08 L0 347.02 L9.59 347.02 L9.59 335.08" class="st3"/>
+			<text x="1.84" y="344.03" class="st11" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>1</text>		</g>
+		<g id="shape99-257" v:mID="99" v:groupContext="shape" transform="translate(53.5505,-147.924)">
+			<title>Sheet.99</title>
+			<path d="M0.59 283.52 L206.27 343.39 L205.7 345.34 L0 285.48 L0.59 283.52 L0.59 283.52 ZM205.85 341.14 L210.88 345.79
+						 L204.14 347.02 L205.85 341.14 L205.85 341.14 Z" class="st12"/>
+		</g>
+		<g id="shape100-259" v:mID="100" v:groupContext="shape" transform="translate(151.516,-147.924)">
+			<title>Sheet.100</title>
+			<path d="M0.59 283.52 L202.41 343.41 L201.83 345.35 L0 285.48 L0.59 283.52 L0.59 283.52 ZM202.01 341.16 L207.01 345.83
+						 L200.27 347.02 L202.01 341.16 L202.01 341.16 Z" class="st13"/>
+		</g>
+		<g id="shape101-261" v:mID="101" v:groupContext="shape" transform="translate(246.975,-147.37)">
+			<title>Sheet.101</title>
+			<path d="M2 283.72 L15.77 341.83 L13.79 342.3 L0 284.18 L2 283.72 L2 283.72 ZM17.53 340.36 L15.97 347.02 L11.57 341.77
+						 L17.53 340.36 L17.53 340.36 Z" class="st12"/>
+		</g>
+		<g id="shape102-263" v:mID="102" v:groupContext="shape" transform="translate(262.972,-147.37)">
+			<title>Sheet.102</title>
+			<path d="M82.31 283.13 L3.45 343.12 L4.68 344.74 L83.54 284.76 L82.31 283.13 L82.31 283.13 ZM3.02 340.89 L0 347.02 L6.74
+						 345.74 L3.02 340.89 L3.02 340.89 Z" class="st12"/>
+		</g>
+		<g id="shape103-265" v:mID="103" v:groupContext="shape" transform="translate(358.537,-149.107)">
+			<title>Sheet.103</title>
+			<path d="M83.92 284.85 L3.53 343.2 L4.73 344.84 L85.12 286.5 L83.92 284.85 L83.92 284.85 ZM3.15 340.95 L0 347.02 L6.75
+						 345.89 L3.15 340.95 L3.15 340.95 Z" class="st13"/>
+		</g>
+		<g id="shape104-267" v:mID="104" v:groupContext="shape" transform="translate(264.413,-147.534)">
+			<title>Sheet.104</title>
+			<path d="M275.95 283 L4.77 343.27 L5.22 345.25 L276.37 285 L275.95 283 L275.95 283 ZM5.31 341.05 L0 345.37 L6.66 347.02
+						 L5.31 341.05 L5.31 341.05 Z" class="st14"/>
+		</g>
+		<g id="shape105-269" v:mID="105" v:groupContext="shape" transform="translate(456.982,-148.103)">
+			<title>Sheet.105</title>
+			<path d="M179.48 283.72 L4.5 343.48 L5.16 345.43 L180.14 285.66 L179.48 283.72 L179.48 283.72 ZM4.8 341.23 L0 346.12
+						 L6.81 347.02 L4.8 341.23 L4.8 341.23 Z" class="st15"/>
+		</g>
+		<g id="shape106-271" v:mID="106" v:groupContext="shape" transform="translate(335.628,-18)">
+			<title>Sheet.106</title>
+			<path d="M0 309.64 C0 305.52 2.99 302.16 6.65 302.16 L14.2 302.16 L8.01 284.85 L35.48 302.16 L78.47 302.16 C82.15 302.16
+						 85.12 305.52 85.12 309.64 L85.12 309.64 L85.12 320.85 L85.12 339.54 C85.12 343.68 82.15 347.02 78.47 347.02
+						 L35.48 347.02 L14.2 347.02 L14.2 347.02 L6.65 347.02 C2.99 347.02 0 343.68 0 339.54 L0 320.85 L0 309.64
+						 L0 309.64 Z" class="st5"/>
+		</g>
+		<g id="shape109-273" v:mID="109" v:groupContext="shape" transform="translate(157.564,-62.4234)">
+			<title>Sheet.109</title>
+			<path d="M16.21 347.02 C11.74 347.02 8.1 346.42 8.1 345.67 L8.1 303.49 C8.1 302.75 4.49 302.14 0 302.14 C4.49 302.14
+						 8.1 301.54 8.1 300.79 L8.1 258.61 C8.1 257.88 11.74 257.26 16.21 257.26" class="st7"/>
+		</g>
+		<g id="shape110-276" v:mID="110" v:groupContext="shape" transform="translate(113.844,-100.157)">
+			<title>Sheet.110</title>
+			<desc>Groups</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="20.2175" cy="341.046" width="40.44" height="11.9384"/>
+			<path d="M40.44 335.08 L0 335.08 L0 347.02 L40.44 347.02 L40.44 335.08" class="st3"/>
+			<text x="3.85" y="344.03" class="st16" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Groups</text>		</g>
+		<g id="shape111-280" v:mID="111" v:groupContext="shape" transform="translate(196.718,-76.2186)">
+			<title>Sheet.111</title>
+			<path d="M0 331.97 C0 330.32 2.27 328.96 5.04 328.96 L37.61 328.96 L60.43 284.85 L53.72 328.96 L59.43 328.96 C62.22 328.96
+						 64.47 330.32 64.47 331.97 L64.47 331.97 L64.47 336.48 L64.47 344.01 C64.47 345.67 62.22 347.02 59.43 347.02
+						 L53.72 347.02 L37.61 347.02 L37.61 347.02 L5.04 347.02 C2.27 347.02 0 345.67 0 344.01 L0 336.48 L0 331.97
+						 L0 331.97 Z" class="st5"/>
+		</g>
+		<g id="shape112-282" v:mID="112" v:groupContext="shape" transform="translate(196.65,-80.2991)">
+			<title>Sheet.112</title>
+			<desc>group id</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="27.7691" cy="339.824" width="55.54" height="14.3829"/>
+			<path d="M55.54 332.63 L0 332.63 L0 347.02 L55.54 347.02 L55.54 332.63" class="st3"/>
+			<text x="5.09" y="343.42" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>group id</text>		</g>
+		<g id="shape114-286" v:mID="114" v:groupContext="shape" transform="translate(506.433,-128.007)">
+			<title>Sheet.114</title>
+			<desc>-</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="6.63728" cy="336.229" width="13.28" height="21.5726"/>
+			<path d="M13.27 325.44 L0 325.44 L0 347.02 L13.27 347.02 L13.27 325.44" class="st3"/>
+			<text x="3.06" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>-</text>		</g>
+		<g id="shape115-290" v:mID="115" v:groupContext="shape" transform="translate(529.004,-128.007)">
+			<title>Sheet.115</title>
+			<desc>Keys separated into</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="97.1729" cy="336.229" width="194.35" height="21.5726"/>
+			<path d="M194.35 325.44 L0 325.44 L0 347.02 L194.35 347.02 L194.35 325.44" class="st3"/>
+			<text x="17.06" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Keys separated into  </text>		</g>
+		<g id="shape116-294" v:mID="116" v:groupContext="shape" transform="translate(529.004,-106.438)">
+			<title>Sheet.116</title>
+			<desc>groups based on</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="83.1587" cy="336.229" width="166.32" height="21.5726"/>
+			<path d="M166.32 325.44 L0 325.44 L0 347.02 L166.32 347.02 L166.32 325.44" class="st3"/>
+			<text x="15.23" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>groups based on  </text>		</g>
+		<g id="shape117-298" v:mID="117" v:groupContext="shape" transform="translate(529.004,-84.869)">
+			<title>Sheet.117</title>
+			<desc>some bits from hash</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="97.731" cy="336.229" width="195.47" height="21.5726"/>
+			<path d="M195.46 325.44 L0 325.44 L0 347.02 L195.46 347.02 L195.46 325.44" class="st3"/>
+			<text x="14.94" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>some bits from hash</text>		</g>
+		<g id="shape118-302" v:mID="118" v:groupContext="shape" transform="translate(506.433,-63.2999)">
+			<title>Sheet.118</title>
+			<desc>-</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="6.63728" cy="336.229" width="13.28" height="21.5726"/>
+			<path d="M13.27 325.44 L0 325.44 L0 347.02 L13.27 347.02 L13.27 325.44" class="st3"/>
+			<text x="3.06" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>-</text>		</g>
+		<g id="shape119-306" v:mID="119" v:groupContext="shape" transform="translate(529.004,-63.2999)">
+			<title>Sheet.119</title>
+			<desc>Groups contain a</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="84.2539" cy="336.229" width="168.51" height="21.5726"/>
+			<path d="M168.51 325.44 L0 325.44 L0 347.02 L168.51 347.02 L168.51 325.44" class="st3"/>
+			<text x="15.38" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Groups contain a  </text>		</g>
+		<g id="shape120-310" v:mID="120" v:groupContext="shape" transform="translate(529.004,-41.7308)">
+			<title>Sheet.120</title>
+			<desc>small number of</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="81.4635" cy="336.229" width="162.93" height="21.5726"/>
+			<path d="M162.93 325.44 L0 325.44 L0 347.02 L162.93 347.02 L162.93 325.44" class="st3"/>
+			<text x="15.01" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>small number of  </text>		</g>
+		<g id="shape121-314" v:mID="121" v:groupContext="shape" transform="translate(529.004,-20.1617)">
+			<title>Sheet.121</title>
+			<desc>keys (&#60;28)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="50.4481" cy="336.229" width="100.9" height="21.5726"/>
+			<path d="M100.9 325.44 L0 325.44 L0 347.02 L100.9 347.02 L100.9 325.44" class="st3"/>
+			<text x="8.77" y="341.62" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>keys (&#60;28)</text>		</g>
+		<g id="shape122-318" v:mID="122" v:groupContext="shape" transform="translate(19.1996,-146.276)">
+			<title>Sheet.122</title>
+			<path d="M0 310.17 C-0 306.1 3.62 302.8 8.07 302.8 L14.46 302.8 L29.68 282.28 L36.14 302.8 L78.65 302.8 C83.11 302.8
+						 86.72 306.1 86.72 310.17 L86.72 310.17 L86.72 321.22 L86.72 339.65 C86.72 343.72 83.11 347.02 78.65 347.02
+						 L36.14 347.02 L14.46 347.02 L14.46 347.02 L8.07 347.02 C3.62 347.02 0 343.72 0 339.65 L0 321.22 L0 310.17
+						 L0 310.17 Z" class="st5"/>
+		</g>
+		<g id="shape123-320" v:mID="123" v:groupContext="shape" transform="translate(41.9777,-174.053)">
+			<title>Sheet.123</title>
+			<desc>Group</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="22.8289" cy="339.824" width="45.66" height="14.3829"/>
+			<path d="M45.66 332.63 L0 332.63 L0 347.02 L45.66 347.02 L45.66 332.63" class="st3"/>
+			<text x="5.9" y="343.42" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group  </text>		</g>
+		<g id="shape124-324" v:mID="124" v:groupContext="shape" transform="translate(34.4142,-159.674)">
+			<title>Sheet.124</title>
+			<desc>Identifier</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="31.5173" cy="339.824" width="63.04" height="14.3829"/>
+			<path d="M63.03 332.63 L0 332.63 L0 347.02 L63.03 347.02 L63.03 332.63" class="st3"/>
+			<text x="7.04" y="343.42" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Identifier  </text>		</g>
+		<g id="shape125-328" v:mID="125" v:groupContext="shape" transform="translate(28.7716,-145.295)">
+			<title>Sheet.125</title>
+			<desc>(simplified)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="36.2165" cy="339.824" width="72.44" height="14.3829"/>
+			<path d="M72.43 332.63 L0 332.63 L0 347.02 L72.43 347.02 L72.43 332.63" class="st3"/>
+			<text x="6.19" y="343.42" class="st17" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(simplified)</text>		</g>
+		<g id="shape127-332" v:mID="127" v:groupContext="shape" transform="translate(517.688,-71.2991)">
+			<title>Sheet.127</title>
+			<desc>Keys separated into groups based on some bits from hash. Grou...</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="112.5" cy="302.139" width="225" height="89.7513"/>
+			<g id="shadow127-333" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+					transform="matrix(1,0,0,1,0.345598,1.97279)" class="st19">
+				<rect x="0" y="257.264" width="225" height="89.7513" class="st20"/>
+			</g>
+			<rect x="0" y="257.264" width="225" height="89.7513" class="st21"/>
+			<text x="4" y="281.09" class="st22" v:langID="1033"><v:paragraph v:indentFirst="-18" v:indentLeft="18" v:bullet="1"/><v:tabList/><tspan
+						class="st23" v:isBullet="true">·</tspan> <tspan class="st24">Keys separated into groups based </tspan><tspan
+						x="22" dy="1.204em" class="st24">on some bits from hash</tspan><tspan class="st24">.<v:newlineChar/></tspan><tspan
+						x="4" dy="1.211em" class="st23" v:isBullet="true">·</tspan> <tspan class="st24">Groups contain a small number of </tspan><tspan
+						x="22" dy="1.204em" class="st24">keys </tspan><tspan class="st24">(</tspan><tspan class="st24">&#60;</tspan><tspan
+						class="st24">28</tspan><tspan class="st24">)</tspan></text>		</g>
+		<g id="shape129-349" v:mID="129" v:groupContext="shape" transform="translate(336.326,-26.2991)">
+			<title>Sheet.129</title>
+			<desc>Total # of keys in group so far</desc>
+			<v:textBlock v:margins="rect(4,4,4,4)"/>
+			<v:textRect cx="39.6784" cy="333.515" width="79.36" height="27"/>
+			<rect x="0" y="320.015" width="79.3567" height="27" class="st25"/>
+			<text x="4.5" y="329.92" class="st26" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Total # of keys <tspan
+						x="4.39" dy="1.2em" class="st23">in group so far</tspan></text>		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i8.svg b/doc/guides/prog_guide/img/efd_i8.svg
new file mode 100644
index 0000000..d0fd463
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i8.svg
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i9.svg Page-2 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="4.98372in" height="2.08442in"
+		viewBox="0 0 358.828 150.078" xml:space="preserve" color-interpolation-filters="sRGB" class="st8">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {fill:#ffffff;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st2 {stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st3 {fill:none;stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st4 {stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st5 {fill:#000000;font-family:Intel Clear;font-size:0.998566em}
+		.st6 {fill:#c00000;font-family:Intel Clear;font-size:0.828804em;font-weight:bold}
+		.st7 {fill:#0071c5;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st8 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<g v:mID="4" v:index="2" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-2</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape4-1" v:mID="4" v:groupContext="shape" transform="translate(206.306,-19.0195)">
+			<title>Sheet.4</title>
+			<path d="M0 38.04 L0 150.08 L133.5 150.08 L133.5 38.04 L0 38.04 L0 38.04 Z" class="st1"/>
+		</g>
+		<g id="shape5-3" v:mID="5" v:groupContext="shape" transform="translate(206.306,-19.0195)">
+			<title>Sheet.5</title>
+			<path d="M0 38.04 L133.5 38.04 L133.5 150.08 L0 150.08 L0 38.04" class="st2"/>
+		</g>
+		<g id="shape6-6" v:mID="6" v:groupContext="shape" transform="translate(215.55,-70.7853)">
+			<title>Sheet.6</title>
+			<path d="M0 121.92 C0 118.82 2.54 116.29 5.64 116.29 L110.69 116.29 C113.81 116.29 116.33 118.82 116.33 121.92 L116.33
+						 144.45 C116.33 147.56 113.81 150.08 110.69 150.08 L5.64 150.08 C2.54 150.08 0 147.56 0 144.45 L0 121.92
+						 Z" class="st1"/>
+		</g>
+		<g id="shape7-8" v:mID="7" v:groupContext="shape" transform="translate(215.55,-70.7853)">
+			<title>Sheet.7</title>
+			<path d="M0 121.92 C0 118.82 2.54 116.29 5.64 116.29 L110.69 116.29 C113.81 116.29 116.33 118.82 116.33 121.92 L116.33
+						 144.45 C116.33 147.56 113.81 150.08 110.69 150.08 L5.64 150.08 C2.54 150.08 0 147.56 0 144.45 L0 121.92
+						 Z" class="st3"/>
+		</g>
+		<g id="shape8-10" v:mID="8" v:groupContext="shape" transform="translate(242.756,-86.1914)">
+			<title>Sheet.8</title>
+			<desc>hash_index</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="36.9951" cy="142.887" width="74" height="14.3829"/>
+			<path d="M73.99 135.7 L0 135.7 L0 150.08 L73.99 150.08 L73.99 135.7" class="st4"/>
+			<text x="6.29" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash_index</text>		</g>
+		<g id="shape9-14" v:mID="9" v:groupContext="shape" transform="translate(229.67,-71.812)">
+			<title>Sheet.9</title>
+			<desc>(integer, 16 bits)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="52.0635" cy="142.887" width="104.13" height="14.3829"/>
+			<path d="M104.13 135.7 L0 135.7 L0 150.08 L104.13 150.08 L104.13 135.7" class="st4"/>
+			<text x="8.25" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(integer, 16 bits)</text>		</g>
+		<g id="shape10-18" v:mID="10" v:groupContext="shape" transform="translate(215.55,-27.1678)">
+			<title>Sheet.10</title>
+			<path d="M0 121.92 C0 118.82 2.54 116.29 5.64 116.29 L110.69 116.29 C113.81 116.29 116.33 118.82 116.33 121.92 L116.33
+						 144.45 C116.33 147.56 113.81 150.08 110.69 150.08 L5.64 150.08 C2.54 150.08 0 147.56 0 144.45 L0 121.92
+						 Z" class="st1"/>
+		</g>
+		<g id="shape11-20" v:mID="11" v:groupContext="shape" transform="translate(215.55,-27.1678)">
+			<title>Sheet.11</title>
+			<path d="M0 121.92 C0 118.82 2.54 116.29 5.64 116.29 L110.69 116.29 C113.81 116.29 116.33 118.82 116.33 121.92 L116.33
+						 144.45 C116.33 147.56 113.81 150.08 110.69 150.08 L5.64 150.08 C2.54 150.08 0 147.56 0 144.45 L0 121.92
+						 Z" class="st3"/>
+		</g>
+		<g id="shape12-22" v:mID="12" v:groupContext="shape" transform="translate(237.836,-42.6033)">
+			<title>Sheet.12</title>
+			<desc>lookup_table</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="42.693" cy="142.887" width="85.39" height="14.3829"/>
+			<path d="M85.39 135.7 L0 135.7 L0 150.08 L85.39 150.08 L85.39 135.7" class="st4"/>
+			<text x="7.03" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>lookup_table</text>		</g>
+		<g id="shape13-26" v:mID="13" v:groupContext="shape" transform="translate(251.643,-28.2239)">
+			<title>Sheet.13</title>
+			<desc>(16 bits)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="26.9562" cy="142.887" width="53.92" height="14.3829"/>
+			<path d="M53.91 135.7 L0 135.7 L0 150.08 L53.91 150.08 L53.91 135.7" class="st4"/>
+			<text x="4.98" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(16 bits)</text>		</g>
+		<g id="shape14-30" v:mID="14" v:groupContext="shape" transform="translate(213.473,-114.303)">
+			<title>Sheet.14</title>
+			<desc>Group ID: 0x0102</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="47.976" cy="144.109" width="95.96" height="11.9384"/>
+			<path d="M95.95 138.14 L0 138.14 L0 150.08 L95.95 150.08 L95.95 138.14" class="st4"/>
+			<text x="7.47" y="147.09" class="st6" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group ID: 0x0102</text>		</g>
+		<g id="shape15-34" v:mID="15" v:groupContext="shape" transform="translate(19.0195,-99.4242)">
+			<title>Sheet.15</title>
+			<path d="M0 129.31 C0 127.02 1.87 125.15 4.16 125.15 L109.18 125.15 C111.47 125.15 113.33 127.02 113.33 129.31 L113.33
+						 145.93 C113.33 148.22 111.47 150.08 109.18 150.08 L4.16 150.08 C1.87 150.08 0 148.22 0 145.93 L0 129.31
+						 Z" class="st1"/>
+		</g>
+		<g id="shape16-36" v:mID="16" v:groupContext="shape" transform="translate(19.0195,-99.4242)">
+			<title>Sheet.16</title>
+			<path d="M0 129.31 C0 127.02 1.87 125.15 4.16 125.15 L109.18 125.15 C111.47 125.15 113.33 127.02 113.33 129.31 L113.33
+						 145.93 C113.33 148.22 111.47 150.08 109.18 150.08 L4.16 150.08 C1.87 150.08 0 148.22 0 145.93 L0 129.31
+						 Z" class="st3"/>
+		</g>
+		<g id="shape17-38" v:mID="17" v:groupContext="shape" transform="translate(33.9485,-103.285)">
+			<title>Sheet.17</title>
+			<desc>Key1: Value = 0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="49.6862" cy="142.887" width="99.38" height="14.3829"/>
+			<path d="M99.37 135.7 L0 135.7 L0 150.08 L99.37 150.08 L99.37 135.7" class="st4"/>
+			<text x="7.94" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key1: Value = 0</text>		</g>
+		<g id="shape18-42" v:mID="18" v:groupContext="shape" transform="translate(19.0195,-74.6198)">
+			<title>Sheet.18</title>
+			<path d="M0 129.31 C0 127.02 1.87 125.15 4.16 125.15 L109.18 125.15 C111.47 125.15 113.33 127.02 113.33 129.31 L113.33
+						 145.93 C113.33 148.22 111.47 150.08 109.18 150.08 L4.16 150.08 C1.87 150.08 0 148.22 0 145.93 L0 129.31
+						 Z" class="st1"/>
+		</g>
+		<g id="shape19-44" v:mID="19" v:groupContext="shape" transform="translate(19.0195,-74.6198)">
+			<title>Sheet.19</title>
+			<path d="M0 129.31 C0 127.02 1.87 125.15 4.16 125.15 L109.18 125.15 C111.47 125.15 113.33 127.02 113.33 129.31 L113.33
+						 145.93 C113.33 148.22 111.47 150.08 109.18 150.08 L4.16 150.08 C1.87 150.08 0 148.22 0 145.93 L0 129.31
+						 Z" class="st3"/>
+		</g>
+		<g id="shape20-46" v:mID="20" v:groupContext="shape" transform="translate(33.9485,-78.4626)">
+			<title>Sheet.20</title>
+			<desc>Key3: Value = 1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="49.6862" cy="142.887" width="99.38" height="14.3829"/>
+			<path d="M99.37 135.7 L0 135.7 L0 150.08 L99.37 150.08 L99.37 135.7" class="st4"/>
+			<text x="7.94" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key3: Value = 1</text>		</g>
+		<g id="shape21-50" v:mID="21" v:groupContext="shape" transform="translate(19.0195,-49.5757)">
+			<title>Sheet.21</title>
+			<path d="M0 129.21 C0 126.91 1.88 125.03 4.19 125.03 L109.15 125.03 C111.46 125.03 113.33 126.91 113.33 129.21 L113.33
+						 145.91 C113.33 148.21 111.46 150.08 109.15 150.08 L4.19 150.08 C1.88 150.08 0 148.21 0 145.91 L0 129.21
+						 Z" class="st1"/>
+		</g>
+		<g id="shape22-52" v:mID="22" v:groupContext="shape" transform="translate(19.0195,-49.5757)">
+			<title>Sheet.22</title>
+			<path d="M0 129.21 C0 126.91 1.88 125.03 4.19 125.03 L109.15 125.03 C111.46 125.03 113.33 126.91 113.33 129.21 L113.33
+						 145.91 C113.33 148.21 111.46 150.08 109.15 150.08 L4.19 150.08 C1.88 150.08 0 148.21 0 145.91 L0 129.21
+						 Z" class="st3"/>
+		</g>
+		<g id="shape23-54" v:mID="23" v:groupContext="shape" transform="translate(33.9485,-53.4903)">
+			<title>Sheet.23</title>
+			<desc>Key4: Value = 0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="49.6862" cy="142.887" width="99.38" height="14.3829"/>
+			<path d="M99.37 135.7 L0 135.7 L0 150.08 L99.37 150.08 L99.37 135.7" class="st4"/>
+			<text x="7.94" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key4: Value = 0</text>		</g>
+		<g id="shape24-58" v:mID="24" v:groupContext="shape" transform="translate(19.0195,-25.0109)">
+			<title>Sheet.24</title>
+			<path d="M0 129.21 C0 126.91 1.88 125.03 4.19 125.03 L109.15 125.03 C111.46 125.03 113.33 126.91 113.33 129.21 L113.33
+						 145.91 C113.33 148.21 111.46 150.08 109.15 150.08 L4.19 150.08 C1.88 150.08 0 148.21 0 145.91 L0 129.21
+						 Z" class="st1"/>
+		</g>
+		<g id="shape25-60" v:mID="25" v:groupContext="shape" transform="translate(19.0195,-25.0109)">
+			<title>Sheet.25</title>
+			<path d="M0 129.21 C0 126.91 1.88 125.03 4.19 125.03 L109.15 125.03 C111.46 125.03 113.33 126.91 113.33 129.21 L113.33
+						 145.91 C113.33 148.21 111.46 150.08 109.15 150.08 L4.19 150.08 C1.88 150.08 0 148.21 0 145.91 L0 129.21
+						 Z" class="st3"/>
+		</g>
+		<g id="shape26-62" v:mID="26" v:groupContext="shape" transform="translate(33.9485,-28.927)">
+			<title>Sheet.26</title>
+			<desc>Key7: Value = 1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="49.6862" cy="142.887" width="99.38" height="14.3829"/>
+			<path d="M99.37 135.7 L0 135.7 L0 150.08 L99.37 150.08 L99.37 135.7" class="st4"/>
+			<text x="7.94" y="146.48" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key7: Value = 1</text>		</g>
+		<g id="shape27-66" v:mID="27" v:groupContext="shape" transform="translate(141.536,-51.5529)">
+			<title>Sheet.27</title>
+			<path d="M0 115.39 L22.75 115.39 L22.75 103.82 L45.5 126.95 L22.75 150.08 L22.75 138.51 L0 138.51 L0 115.39 Z"
+					class="st7"/>
+		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/img/efd_i9.svg b/doc/guides/prog_guide/img/efd_i9.svg
new file mode 100644
index 0000000..b2e385d
--- /dev/null
+++ b/doc/guides/prog_guide/img/efd_i9.svg
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i10.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="9.8125in" height="3.76365in"
+		viewBox="0 0 706.5 270.983" xml:space="preserve" color-interpolation-filters="sRGB" class="st9">
+	<v:documentProperties v:langID="1033" v:viewMarkup="false">
+		<v:userDefs>
+			<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+			<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+		</v:userDefs>
+	</v:documentProperties>
+
+	<style type="text/css">
+	<![CDATA[
+		.st1 {fill:#ffffff;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st2 {fill:none;stroke:#00aeef;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.03901}
+		.st3 {stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st4 {fill:#000000;font-family:Arial;font-size:0.998566em}
+		.st5 {fill:#000000;font-family:Arial;font-size:0.918686em}
+		.st6 {fill:#0071c5;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+		.st7 {fill:#ffffff;font-family:Arial;font-size:0.998566em}
+		.st8 {fill:#ffffff;font-family:Arial;font-size:1.49785em}
+		.st9 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+	]]>
+	</style>
+
+	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+		<v:userDefs>
+			<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+		</v:userDefs>
+		<title>Page-1</title>
+		<v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+		<g id="shape68-1" v:mID="68" v:groupContext="shape" transform="translate(196.523,-158.978)">
+			<title>Sheet.68</title>
+			<path d="M0 250.22 C0 247.95 1.89 246.06 4.17 246.06 L317.25 246.06 C319.53 246.06 321.39 247.95 321.39 250.22 L321.39
+						 266.85 C321.39 269.13 319.53 270.98 317.25 270.98 L4.17 270.98 C1.89 270.98 0 269.13 0 266.85 L0 250.22
+						 Z" class="st1"/>
+		</g>
+		<g id="shape69-3" v:mID="69" v:groupContext="shape" transform="translate(196.523,-158.978)">
+			<title>Sheet.69</title>
+			<path d="M0 250.22 C0 247.95 1.89 246.06 4.17 246.06 L317.25 246.06 C319.53 246.06 321.39 247.95 321.39 250.22 L321.39
+						 266.85 C321.39 269.13 319.53 270.98 317.25 270.98 L4.17 270.98 C1.89 270.98 0 269.13 0 266.85 L0 250.22
+						 Z" class="st2"/>
+		</g>
+		<g id="shape70-5" v:mID="70" v:groupContext="shape" transform="translate(186.139,-162.437)">
+			<title>Sheet.70</title>
+			<desc>(hash(key, seed1) + hash_index *</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="106.671" cy="263.792" width="213.35" height="14.3829"/>
+			<path d="M213.34 256.6 L0 256.6 L0 270.98 L213.34 270.98 L213.34 256.6" class="st3"/>
+			<text x="17.24" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>(hash(key, seed1) + hash_index *  </text>		</g>
+		<g id="shape71-9" v:mID="71" v:groupContext="shape" transform="translate(381.48,-162.845)">
+			<title>Sheet.71</title>
+			<desc>hash(key</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="27.4843" cy="264.367" width="54.97" height="13.2327"/>
+			<path d="M54.97 257.75 L0 257.75 L0 270.98 L54.97 270.98 L54.97 257.75" class="st3"/>
+			<text x="5.12" y="267.67" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash(key</text>		</g>
+		<g id="shape72-13" v:mID="72" v:groupContext="shape" transform="translate(424.755,-162.437)">
+			<title>Sheet.72</title>
+			<desc>, seed2)) % 16</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="46.7254" cy="263.792" width="93.46" height="14.3829"/>
+			<path d="M93.45 256.6 L0 256.6 L0 270.98 L93.45 270.98 L93.45 256.6" class="st3"/>
+			<text x="7.76" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>, seed2)) % 16</text>		</g>
+		<g id="shape73-17" v:mID="73" v:groupContext="shape" transform="translate(524.094,-148.373)">
+			<title>Sheet.73</title>
+			<path d="M0 236.29 L22.75 236.29 L22.75 224.73 L45.5 247.86 L22.75 270.98 L22.75 259.42 L0 259.42 L0 236.29 Z"
+					class="st6"/>
+		</g>
+		<g id="shape74-19" v:mID="74" v:groupContext="shape" transform="translate(574.148,-217.574)">
+			<title>Sheet.74</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st1"/>
+		</g>
+		<g id="shape75-21" v:mID="75" v:groupContext="shape" transform="translate(574.148,-217.574)">
+			<title>Sheet.75</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st2"/>
+		</g>
+		<g id="shape76-23" v:mID="76" v:groupContext="shape" transform="translate(584.296,-231.499)">
+			<title>Sheet.76</title>
+			<desc>lookup_table</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="40.993" cy="263.792" width="81.99" height="14.3829"/>
+			<path d="M81.99 256.6 L0 256.6 L0 270.98 L81.99 270.98 L81.99 256.6" class="st3"/>
+			<text x="7.01" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>lookup_table</text>		</g>
+		<g id="shape77-27" v:mID="77" v:groupContext="shape" transform="translate(655.369,-231.499)">
+			<title>Sheet.77</title>
+			<desc>bit</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="11.1076" cy="263.792" width="22.22" height="14.3829"/>
+			<path d="M22.22 256.6 L0 256.6 L0 270.98 L22.22 270.98 L22.22 256.6" class="st3"/>
+			<text x="4.78" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>bit  </text>		</g>
+		<g id="shape78-31" v:mID="78" v:groupContext="shape" transform="translate(588.858,-217.12)">
+			<title>Sheet.78</title>
+			<desc>index for key1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="44.8113" cy="263.792" width="89.63" height="14.3829"/>
+			<path d="M89.62 256.6 L0 256.6 L0 270.98 L89.62 270.98 L89.62 256.6" class="st3"/>
+			<text x="7.51" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>index for key1</text>		</g>
+		<g id="shape79-35" v:mID="79" v:groupContext="shape" transform="translate(573.548,-178.869)">
+			<title>Sheet.79</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st1"/>
+		</g>
+		<g id="shape80-37" v:mID="80" v:groupContext="shape" transform="translate(573.548,-178.869)">
+			<title>Sheet.80</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st2"/>
+		</g>
+		<g id="shape81-39" v:mID="81" v:groupContext="shape" transform="translate(584.296,-192.768)">
+			<title>Sheet.81</title>
+			<desc>lookup_table</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="40.993" cy="263.792" width="81.99" height="14.3829"/>
+			<path d="M81.99 256.6 L0 256.6 L0 270.98 L81.99 270.98 L81.99 256.6" class="st3"/>
+			<text x="7.01" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>lookup_table</text>		</g>
+		<g id="shape82-43" v:mID="82" v:groupContext="shape" transform="translate(655.369,-192.768)">
+			<title>Sheet.82</title>
+			<desc>bit</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="11.1076" cy="263.792" width="22.22" height="14.3829"/>
+			<path d="M22.22 256.6 L0 256.6 L0 270.98 L22.22 270.98 L22.22 256.6" class="st3"/>
+			<text x="4.78" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>bit  </text>		</g>
+		<g id="shape83-47" v:mID="83" v:groupContext="shape" transform="translate(588.858,-178.388)">
+			<title>Sheet.83</title>
+			<desc>index for key3</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="44.8113" cy="263.792" width="89.63" height="14.3829"/>
+			<path d="M89.62 256.6 L0 256.6 L0 270.98 L89.62 270.98 L89.62 256.6" class="st3"/>
+			<text x="7.51" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>index for key3</text>		</g>
+		<g id="shape84-51" v:mID="84" v:groupContext="shape" transform="translate(574.148,-139.326)">
+			<title>Sheet.84</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st1"/>
+		</g>
+		<g id="shape85-53" v:mID="85" v:groupContext="shape" transform="translate(574.148,-139.326)">
+			<title>Sheet.85</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st2"/>
+		</g>
+		<g id="shape86-55" v:mID="86" v:groupContext="shape" transform="translate(584.296,-153.227)">
+			<title>Sheet.86</title>
+			<desc>lookup_table</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="40.993" cy="263.792" width="81.99" height="14.3829"/>
+			<path d="M81.99 256.6 L0 256.6 L0 270.98 L81.99 270.98 L81.99 256.6" class="st3"/>
+			<text x="7.01" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>lookup_table</text>		</g>
+		<g id="shape87-59" v:mID="87" v:groupContext="shape" transform="translate(655.369,-153.227)">
+			<title>Sheet.87</title>
+			<desc>bit</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="11.1076" cy="263.792" width="22.22" height="14.3829"/>
+			<path d="M22.22 256.6 L0 256.6 L0 270.98 L22.22 270.98 L22.22 256.6" class="st3"/>
+			<text x="4.78" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>bit  </text>		</g>
+		<g id="shape88-63" v:mID="88" v:groupContext="shape" transform="translate(588.858,-138.848)">
+			<title>Sheet.88</title>
+			<desc>index for key4</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="44.8113" cy="263.792" width="89.63" height="14.3829"/>
+			<path d="M89.62 256.6 L0 256.6 L0 270.98 L89.62 270.98 L89.62 256.6" class="st3"/>
+			<text x="7.51" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>index for key4</text>		</g>
+		<g id="shape89-67" v:mID="89" v:groupContext="shape" transform="translate(574.148,-100.622)">
+			<title>Sheet.89</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st1"/>
+		</g>
+		<g id="shape90-69" v:mID="90" v:groupContext="shape" transform="translate(574.148,-100.622)">
+			<title>Sheet.90</title>
+			<path d="M0 244.83 C-0 241.95 2.37 239.59 5.25 239.59 L108.11 239.59 C110.99 239.59 113.33 241.95 113.33 244.83 L113.33
+						 265.77 C113.33 268.65 110.99 270.98 108.11 270.98 L5.25 270.98 C2.37 270.98 0 268.65 0 265.77 L0 244.83
+						 Z" class="st2"/>
+		</g>
+		<g id="shape91-71" v:mID="91" v:groupContext="shape" transform="translate(584.296,-114.496)">
+			<title>Sheet.91</title>
+			<desc>lookup_table</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="40.993" cy="263.792" width="81.99" height="14.3829"/>
+			<path d="M81.99 256.6 L0 256.6 L0 270.98 L81.99 270.98 L81.99 256.6" class="st3"/>
+			<text x="7.01" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>lookup_table</text>		</g>
+		<g id="shape92-75" v:mID="92" v:groupContext="shape" transform="translate(655.369,-114.496)">
+			<title>Sheet.92</title>
+			<desc>bit</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="11.1076" cy="263.792" width="22.22" height="14.3829"/>
+			<path d="M22.22 256.6 L0 256.6 L0 270.98 L22.22 270.98 L22.22 256.6" class="st3"/>
+			<text x="4.78" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>bit  </text>		</g>
+		<g id="shape93-79" v:mID="93" v:groupContext="shape" transform="translate(588.858,-100.117)">
+			<title>Sheet.93</title>
+			<desc>index for key7</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="44.8113" cy="263.792" width="89.63" height="14.3829"/>
+			<path d="M89.62 256.6 L0 256.6 L0 270.98 L89.62 270.98 L89.62 256.6" class="st3"/>
+			<text x="7.51" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>index for key7</text>		</g>
+		<g id="shape94-83" v:mID="94" v:groupContext="shape" transform="translate(205.227,-191.137)">
+			<title>Sheet.94</title>
+			<path d="M0 217.76 C0 213 3.87 209.14 8.64 209.14 L14.53 209.14 L14.53 209.14 L36.32 209.14 L78.52 209.14 C83.3 209.14
+						 87.16 213 87.16 217.76 L87.16 239.33 L87.16 239.33 L87.16 252.27 L87.16 252.27 C87.16 257.05 83.3 260.9
+						 78.52 260.9 L36.32 260.9 L18.46 270.98 L14.53 260.9 L8.64 260.9 C3.87 260.9 0 257.05 0 252.27 L0 239.33
+						 L0 239.33 L0 217.76 Z" class="st6"/>
+		</g>
+		<g id="shape95-85" v:mID="95" v:groupContext="shape" transform="translate(214.98,-225.215)">
+			<title>Sheet.95</title>
+			<desc>CRC32 (32</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="38.2947" cy="263.792" width="76.59" height="14.3829"/>
+			<path d="M76.59 256.6 L0 256.6 L0 270.98 L76.59 270.98 L76.59 256.6" class="st3"/>
+			<text x="8.33" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>CRC32 (32  </text>		</g>
+		<g id="shape96-89" v:mID="96" v:groupContext="shape" transform="translate(222.123,-210.835)">
+			<title>Sheet.96</title>
+			<desc>bit output)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="32.5584" cy="263.792" width="65.12" height="14.3829"/>
+			<path d="M65.12 256.6 L0 256.6 L0 270.98 L65.12 270.98 L65.12 256.6" class="st3"/>
+			<text x="5.91" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>bit output)</text>		</g>
+		<g id="shape97-93" v:mID="97" v:groupContext="shape" transform="translate(305.473,-188.366)">
+			<title>Sheet.97</title>
+			<path d="M0 226.84 C0 223.28 2.9 220.39 6.47 220.39 L21.37 220.39 L21.37 220.39 L53.42 220.39 L121.77 220.39 C125.34
+						 220.39 128.22 223.28 128.22 226.84 L128.22 242.97 L128.22 242.97 L128.22 252.65 L128.22 252.65 C128.22 256.21
+						 125.34 259.09 121.77 259.09 L53.42 259.09 L38.73 270.98 L21.37 259.09 L6.47 259.09 C2.9 259.09 0 256.21
+						 0 252.65 L0 242.97 L0 242.97 L0 226.84 Z" class="st6"/>
+		</g>
+		<g id="shape98-95" v:mID="98" v:groupContext="shape" transform="translate(318.48,-217.733)">
+			<title>Sheet.98</title>
+			<desc>Goal: Find a valid</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="57.4478" cy="263.792" width="114.9" height="14.3829"/>
+			<path d="M114.9 256.6 L0 256.6 L0 270.98 L114.9 270.98 L114.9 256.6" class="st3"/>
+			<text x="10.82" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Goal: Find a valid  </text>		</g>
+		<g id="shape99-99" v:mID="99" v:groupContext="shape" transform="translate(339.077,-203.354)">
+			<title>Sheet.99</title>
+			<desc>hash_index</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="37.1611" cy="263.792" width="74.33" height="14.3829"/>
+			<path d="M74.32 256.6 L0 256.6 L0 270.98 L74.32 270.98 L74.32 256.6" class="st3"/>
+			<text x="6.51" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>hash_index</text>		</g>
+		<g id="shape100-103" v:mID="100" v:groupContext="shape" transform="translate(438.135,-185.939)">
+			<title>Sheet.100</title>
+			<path d="M0 217.36 C0 213.8 2.91 210.89 6.48 210.89 L21.37 210.89 L21.37 210.89 L53.42 210.89 L121.77 210.89 C125.34
+						 210.89 128.22 213.8 128.22 217.36 L128.22 233.48 L128.22 233.48 L128.22 243.15 L128.22 243.15 C128.22 246.72
+						 125.34 249.59 121.77 249.59 L53.42 249.59 L54.75 270.98 L21.37 249.59 L6.48 249.59 C2.91 249.59 0 246.72
+						 0 243.15 L0 233.48 L0 233.48 L0 217.36 Z" class="st6"/>
+		</g>
+		<g id="shape101-105" v:mID="101" v:groupContext="shape" transform="translate(448.763,-224.802)">
+			<title>Sheet.101</title>
+			<desc>Lookup Table has</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="58.6085" cy="263.792" width="117.22" height="14.3829"/>
+			<path d="M117.22 256.6 L0 256.6 L0 270.98 L117.22 270.98 L117.22 256.6" class="st3"/>
+			<text x="10.98" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Lookup Table has  </text>		</g>
+		<g id="shape102-109" v:mID="102" v:groupContext="shape" transform="translate(484.549,-210.423)">
+			<title>Sheet.102</title>
+			<desc>16 bits</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="22.2166" cy="263.792" width="44.44" height="14.3829"/>
+			<path d="M44.43 256.6 L0 256.6 L0 270.98 L44.43 270.98 L44.43 256.6" class="st3"/>
+			<text x="4.56" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>16 bits</text>		</g>
+		<g id="shape103-113" v:mID="103" v:groupContext="shape" transform="translate(369.583,-90.8555)">
+			<title>Sheet.103</title>
+			<path d="M0 227.76 C0 222.98 3.89 219.1 8.67 219.1 L14.53 219.1 L34.47 205.09 L36.32 219.1 L78.5 219.1 C83.29 219.1 87.16
+						 222.98 87.16 227.76 L87.16 227.76 L87.16 240.73 L87.16 262.34 C87.16 267.12 83.29 270.98 78.5 270.98 L36.32
+						 270.98 L14.53 270.98 L14.53 270.98 L8.67 270.98 C3.89 270.98 0 267.12 0 262.34 L0 240.73 L0 227.76 L0 227.76
+						 Z" class="st6"/>
+		</g>
+		<g id="shape104-115" v:mID="104" v:groupContext="shape" transform="translate(383.264,-114.932)">
+			<title>Sheet.104</title>
+			<desc>CRC32 (32</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="38.2947" cy="263.792" width="76.59" height="14.3829"/>
+			<path d="M76.59 256.6 L0 256.6 L0 270.98 L76.59 270.98 L76.59 256.6" class="st3"/>
+			<text x="8.33" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>CRC32 (32  </text>		</g>
+		<g id="shape105-119" v:mID="105" v:groupContext="shape" transform="translate(386.505,-100.553)">
+			<title>Sheet.105</title>
+			<desc>bit output)</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="32.5584" cy="263.792" width="65.12" height="14.3829"/>
+			<path d="M65.12 256.6 L0 256.6 L0 270.98 L65.12 270.98 L65.12 256.6" class="st3"/>
+			<text x="5.91" y="267.39" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>bit output)</text>		</g>
+		<g id="shape106-123" v:mID="106" v:groupContext="shape" transform="translate(313.397,-18)">
+			<title>Sheet.106</title>
+			<path d="M0 226.35 C0 221.43 4.02 217.42 8.94 217.42 L347.02 217.42 C351.97 217.42 355.96 221.43 355.96 226.35 L355.96
+						 262.06 C355.96 267 351.97 270.98 347.02 270.98 L8.94 270.98 C4.02 270.98 0 267 0 262.06 L0 226.35 Z"
+					class="st6"/>
+		</g>
+		<g id="shape107-125" v:mID="107" v:groupContext="shape" transform="translate(313.98,-41.963)">
+			<title>Sheet.107</title>
+			<desc>Goal is to find a hash_index that produces</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="177.75" cy="260.197" width="355.5" height="21.5726"/>
+			<path d="M355.5 249.41 L0 249.41 L0 270.98 L355.5 270.98 L355.5 249.41" class="st3"/>
+			<text x="9.88" y="265.59" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Goal is to find a hash_index that produces  </text>		</g>
+		<g id="shape108-129" v:mID="108" v:groupContext="shape" transform="translate(318.48,-20.3939)">
+			<title>Sheet.108</title>
+			<desc>a lookup_table with no contradictions</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="175.5" cy="260.197" width="351" height="21.5726"/>
+			<path d="M351 249.41 L0 249.41 L0 270.98 L351 270.98 L351 249.41" class="st3"/>
+			<text x="28.12" y="265.59" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>a lookup_table with no contradictions</text>		</g>
+		<g id="shape109-133" v:mID="109" v:groupContext="shape" transform="translate(18,-196.244)">
+			<title>Sheet.109</title>
+			<path d="M0 250.22 C0 247.92 1.87 246.06 4.16 246.06 L109.18 246.06 C111.47 246.06 113.33 247.92 113.33 250.22 L113.33
+						 266.83 C113.33 269.13 111.47 270.98 109.18 270.98 L4.16 270.98 C1.87 270.98 0 269.13 0 266.83 L0 250.22
+						 Z" class="st1"/>
+		</g>
+		<g id="shape110-135" v:mID="110" v:groupContext="shape" transform="translate(29.8201,-196.244)">
+			<title>Sheet.110</title>
+			<path d="M0 250.22 C-0 247.92 1.67 246.06 3.73 246.06 L97.79 246.06 C99.85 246.06 101.51 247.92 101.51 250.22 L101.51
+						 266.83 C101.51 269.13 99.85 270.98 97.79 270.98 L3.73 270.98 C1.67 270.98 0 269.13 0 266.83 L0 250.22 Z"
+					class="st2"/>
+		</g>
+		<g id="shape111-137" v:mID="111" v:groupContext="shape" transform="translate(32.5663,-199.746)">
+			<title>Sheet.111</title>
+			<desc>Key1: Value = 0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="50.7562" cy="263.792" width="101.52" height="14.3829"/>
+			<path d="M101.51 256.6 L0 256.6 L0 270.98 L101.51 270.98 L101.51 256.6" class="st3"/>
+			<text x="8.29" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key1: Value = 0</text>		</g>
+		<g id="shape112-141" v:mID="112" v:groupContext="shape" transform="translate(18,-171.44)">
+			<title>Sheet.112</title>
+			<path d="M0 250.22 C0 247.92 1.87 246.06 4.16 246.06 L109.18 246.06 C111.47 246.06 113.33 247.92 113.33 250.22 L113.33
+						 266.83 C113.33 269.13 111.47 270.98 109.18 270.98 L4.16 270.98 C1.87 270.98 0 269.13 0 266.83 L0 250.22
+						 Z" class="st1"/>
+		</g>
+		<g id="shape113-143" v:mID="113" v:groupContext="shape" transform="translate(29.8201,-171.44)">
+			<title>Sheet.113</title>
+			<path d="M0 250.22 C0 247.92 1.67 246.06 3.73 246.06 L97.79 246.06 C99.85 246.06 101.51 247.92 101.51 250.22 L101.51
+						 266.83 C101.51 269.13 99.85 270.98 97.79 270.98 L3.73 270.98 C1.67 270.98 0 269.13 0 266.83 L0 250.22 Z"
+					class="st2"/>
+		</g>
+		<g id="shape114-145" v:mID="114" v:groupContext="shape" transform="translate(32.5663,-174.923)">
+			<title>Sheet.114</title>
+			<desc>Key3: Value = 1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="50.7562" cy="263.792" width="101.52" height="14.3829"/>
+			<path d="M101.51 256.6 L0 256.6 L0 270.98 L101.51 270.98 L101.51 256.6" class="st3"/>
+			<text x="8.29" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key3: Value = 1</text>		</g>
+		<g id="shape115-149" v:mID="115" v:groupContext="shape" transform="translate(18,-146.396)">
+			<title>Sheet.115</title>
+			<path d="M0 250.12 C0 247.81 1.88 245.94 4.19 245.94 L109.15 245.94 C111.46 245.94 113.33 247.81 113.33 250.12 L113.33
+						 266.81 C113.33 269.12 111.46 270.98 109.15 270.98 L4.19 270.98 C1.88 270.98 0 269.12 0 266.81 L0 250.12
+						 Z" class="st1"/>
+		</g>
+		<g id="shape116-151" v:mID="116" v:groupContext="shape" transform="translate(29.8201,-146.396)">
+			<title>Sheet.116</title>
+			<path d="M0 250.12 C0 247.81 1.68 245.94 3.75 245.94 L97.77 245.94 C99.84 245.94 101.51 247.81 101.51 250.12 L101.51
+						 266.81 C101.51 269.12 99.84 270.98 97.77 270.98 L3.75 270.98 C1.68 270.98 0 269.12 0 266.81 L0 250.12 Z"
+					class="st2"/>
+		</g>
+		<g id="shape117-153" v:mID="117" v:groupContext="shape" transform="translate(32.5663,-149.951)">
+			<title>Sheet.117</title>
+			<desc>Key4: Value = 0</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="50.7562" cy="263.792" width="101.52" height="14.3829"/>
+			<path d="M101.51 256.6 L0 256.6 L0 270.98 L101.51 270.98 L101.51 256.6" class="st3"/>
+			<text x="8.29" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key4: Value = 0</text>		</g>
+		<g id="shape118-157" v:mID="118" v:groupContext="shape" transform="translate(18,-121.831)">
+			<title>Sheet.118</title>
+			<path d="M0 250.12 C0 247.81 1.88 245.94 4.19 245.94 L109.15 245.94 C111.46 245.94 113.33 247.81 113.33 250.12 L113.33
+						 266.81 C113.33 269.12 111.46 270.98 109.15 270.98 L4.19 270.98 C1.88 270.98 0 269.12 0 266.81 L0 250.12
+						 Z" class="st1"/>
+		</g>
+		<g id="shape119-159" v:mID="119" v:groupContext="shape" transform="translate(29.8201,-121.831)">
+			<title>Sheet.119</title>
+			<path d="M0 250.12 C0 247.81 1.68 245.94 3.75 245.94 L97.77 245.94 C99.84 245.94 101.51 247.81 101.51 250.12 L101.51
+						 266.81 C101.51 269.12 99.84 270.98 97.77 270.98 L3.75 270.98 C1.68 270.98 0 269.12 0 266.81 L0 250.12 Z"
+					class="st2"/>
+		</g>
+		<g id="shape120-161" v:mID="120" v:groupContext="shape" transform="translate(32.5663,-125.388)">
+			<title>Sheet.120</title>
+			<desc>Key7: Value = 1</desc>
+			<v:textBlock v:margins="rect(0,0,0,0)"/>
+			<v:textRect cx="50.7562" cy="263.792" width="101.52" height="14.3829"/>
+			<path d="M101.51 256.6 L0 256.6 L0 270.98 L101.51 270.98 L101.51 256.6" class="st3"/>
+			<text x="8.29" y="267.39" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key7: Value = 1</text>		</g>
+		<g id="shape121-165" v:mID="121" v:groupContext="shape" transform="translate(140.517,-148.373)">
+			<title>Sheet.121</title>
+			<path d="M0 236.29 L22.75 236.29 L22.75 224.73 L45.5 247.86 L22.75 270.98 L22.75 259.42 L0 259.42 L0 236.29 Z"
+					class="st6"/>
+		</g>
+	</g>
+</svg>
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index ed7f770..7f825cb 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -47,6 +47,7 @@ Programmer's Guide
     link_bonding_poll_mode_drv_lib
     timer_lib
     hash_lib
+    efd_lib
     lpm_lib
     lpm6_lib
     packet_distrib_lib
@@ -167,6 +168,28 @@ Programmer's Guide
 
 :numref:`figure_figure39` :ref:`figure_figure39`
 
+:numref:`figure_efd1` :ref:`figure_efd1`
+
+:numref:`figure_efd2` :ref:`figure_efd2`
+
+:numref:`figure_efd3` :ref:`figure_efd3`
+
+:numref:`figure_efd4` :ref:`figure_efd4`
+
+:numref:`figure_efd5` :ref:`figure_efd5`
+
+:numref:`figure_efd6` :ref:`figure_efd6`
+
+:numref:`figure_efd7` :ref:`figure_efd7`
+
+:numref:`figure_efd8` :ref:`figure_efd8`
+
+:numref:`figure_efd9` :ref:`figure_efd9`
+
+:numref:`figure_efd10` :ref:`figure_efd10`
+
+:numref:`figure_efd11` :ref:`figure_efd11`
+
 
 **Tables**
 
diff --git a/doc/guides/rel_notes/release_17_02.rst b/doc/guides/rel_notes/release_17_02.rst
index 5023038..4c94513 100644
--- a/doc/guides/rel_notes/release_17_02.rst
+++ b/doc/guides/rel_notes/release_17_02.rst
@@ -64,6 +64,9 @@ New Features
   is much smaller than a hash-based flow table and therefore, it can better fit for
   CPU cache, being able to scale to millions of flow keys.
 
+  See the :ref:`Elastic Flow Distributor Library <Efd_Library>` documentation in
+  the Programmers Guide document, for more information.
+
 
 Resolved Issues
 ---------------
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 3/5] examples/flow_distributor: sample app to demonstrate EFD usage
From: Pablo de Lara @ 2017-01-15 12:04 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara, Saikrishna Edupuganti
In-Reply-To: <1484481875-126335-1-git-send-email-pablo.de.lara.guarch@intel.com>

This new sample app, based on the client/server sample app,
shows the user an scenario using the EFD library.
It consists of:

- A front-end server which has an EFD table that stores the
  node id for each flow key, which will distribute the incoming
  packets to the different nodes

- A back-end node, which has a hash table where node checks,
  after reading packets coming from the server, whether the packet
  is meant to be used in such node, in which case it will be TXed,
  or not, in which case, packet will be dropped.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Saikrishna Edupuganti <saikrishna.edupuganti@intel.com>
Acked-by: Christian Maciocco <christian.maciocco@intel.com>
---
 MAINTAINERS                                    |   1 +
 doc/api/examples.dox                           |   4 +
 examples/Makefile                              |   1 +
 examples/flow_distributor/Makefile             |  44 +++
 examples/flow_distributor/distributor/Makefile |  57 ++++
 examples/flow_distributor/distributor/args.c   | 200 ++++++++++++
 examples/flow_distributor/distributor/args.h   |  39 +++
 examples/flow_distributor/distributor/init.c   | 371 ++++++++++++++++++++++
 examples/flow_distributor/distributor/init.h   |  76 +++++
 examples/flow_distributor/distributor/main.c   | 362 +++++++++++++++++++++
 examples/flow_distributor/node/Makefile        |  48 +++
 examples/flow_distributor/node/node.c          | 417 +++++++++++++++++++++++++
 examples/flow_distributor/shared/common.h      |  99 ++++++
 13 files changed, 1719 insertions(+)
 create mode 100644 examples/flow_distributor/Makefile
 create mode 100644 examples/flow_distributor/distributor/Makefile
 create mode 100644 examples/flow_distributor/distributor/args.c
 create mode 100644 examples/flow_distributor/distributor/args.h
 create mode 100644 examples/flow_distributor/distributor/init.c
 create mode 100644 examples/flow_distributor/distributor/init.h
 create mode 100644 examples/flow_distributor/distributor/main.c
 create mode 100644 examples/flow_distributor/node/Makefile
 create mode 100644 examples/flow_distributor/node/node.c
 create mode 100644 examples/flow_distributor/shared/common.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d812962..b124f6e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -533,6 +533,7 @@ M: Byron Marohn <byron.marohn@intel.com>
 M: Pablo de Lara Guarch <pablo.de.lara.guarch@intel.com>
 F: lib/librte_efd/
 F: app/test/test_efd*
+F: examples/flow_distributor/
 
 Hashes
 M: Bruce Richardson <bruce.richardson@intel.com>
diff --git a/doc/api/examples.dox b/doc/api/examples.dox
index 1626852..c13e574 100644
--- a/doc/api/examples.dox
+++ b/doc/api/examples.dox
@@ -52,6 +52,10 @@
 @example load_balancer/init.c
 @example load_balancer/main.c
 @example load_balancer/runtime.c
+@example flow_distributor/distributor/args.c
+@example flow_distributor/distributor/init.c
+@example flow_distributor/distributor/main.c
+@example flow_distributor/node/node.c
 @example multi_process/client_server_mp/mp_client/client.c
 @example multi_process/client_server_mp/mp_server/args.c
 @example multi_process/client_server_mp/mp_server/init.c
diff --git a/examples/Makefile b/examples/Makefile
index d49c7f2..b404982 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -45,6 +45,7 @@ DIRS-y += dpdk_qat
 endif
 DIRS-y += ethtool
 DIRS-y += exception_path
+DIRS-$(CONFIG_RTE_LIBRTE_EFD) += flow_distributor
 DIRS-y += helloworld
 DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += ip_pipeline
 ifeq ($(CONFIG_RTE_LIBRTE_LPM),y)
diff --git a/examples/flow_distributor/Makefile b/examples/flow_distributor/Makefile
new file mode 100644
index 0000000..5bae706
--- /dev/null
+++ b/examples/flow_distributor/Makefile
@@ -0,0 +1,44 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += distributor
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += node
+
+include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/flow_distributor/distributor/Makefile b/examples/flow_distributor/distributor/Makefile
new file mode 100644
index 0000000..8714151
--- /dev/null
+++ b/examples/flow_distributor/distributor/Makefile
@@ -0,0 +1,57 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV), "linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = distributor
+
+# all source are stored in SRCS-y
+SRCS-y := main.c init.c args.c
+
+INC := $(wildcard *.h)
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/flow_distributor/distributor/args.c b/examples/flow_distributor/distributor/args.c
new file mode 100644
index 0000000..ee29203
--- /dev/null
+++ b/examples/flow_distributor/distributor/args.c
@@ -0,0 +1,200 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <rte_memory.h>
+#include <rte_string_fns.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+/* 1M flows by default */
+#define DEFAULT_NUM_FLOWS    0x100000
+
+/* global var for number of nodes - extern in header */
+uint8_t num_nodes;
+/* global var for number of flows - extern in header */
+uint32_t num_flows = DEFAULT_NUM_FLOWS;
+
+static const char *progname;
+
+/**
+ * Prints out usage information to stdout
+ */
+static void
+usage(void)
+{
+	printf("%s [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS\n"
+		" -p PORTMASK: hexadecimal bitmask of ports to use\n"
+		" -n NUM_NODES: number of node processes to use\n"
+		" -f NUM_FLOWS: number of flows to be added in the EFD table\n",
+		progname);
+}
+
+/**
+ * The ports to be used by the application are passed in
+ * the form of a bitmask. This function parses the bitmask
+ * and places the port numbers to be used into the port[]
+ * array variable
+ */
+static int
+parse_portmask(uint8_t max_ports, const char *portmask)
+{
+	char *end = NULL;
+	unsigned long pm;
+	uint8_t count = 0;
+
+	if (portmask == NULL || *portmask == '\0')
+		return -1;
+
+	/* convert parameter to a number and verify */
+	pm = strtoul(portmask, &end, 16);
+	if (end == NULL || *end != '\0' || pm == 0)
+		return -1;
+
+	/* loop through bits of the mask and mark ports */
+	while (pm != 0) {
+		if (pm & 0x01) { /* bit is set in mask, use port */
+			if (count >= max_ports)
+				printf("WARNING: requested port %u not present"
+				" - ignoring\n", (unsigned int)count);
+			else
+			    info->id[info->num_ports++] = count;
+		}
+		pm = (pm >> 1);
+		count++;
+	}
+
+	return 0;
+}
+
+/**
+ * Take the number of nodes parameter passed to the app
+ * and convert to a number to store in the num_nodes variable
+ */
+static int
+parse_num_nodes(const char *nodes)
+{
+	char *end = NULL;
+	unsigned long temp;
+
+	if (nodes == NULL || *nodes == '\0')
+		return -1;
+
+	temp = strtoul(nodes, &end, 10);
+	if (end == NULL || *end != '\0' || temp == 0)
+		return -1;
+
+	num_nodes = (uint8_t)temp;
+	return 0;
+}
+
+static int
+parse_num_flows(const char *flows)
+{
+	char *end = NULL;
+
+	/* parse hexadecimal string */
+	num_flows = strtoul(flows, &end, 16);
+	if ((flows[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	if (num_flows == 0)
+		return -1;
+
+	return 0;
+}
+
+/**
+ * The application specific arguments follow the DPDK-specific
+ * arguments which are stripped by the DPDK init. This function
+ * processes these application arguments, printing usage info
+ * on error.
+ */
+int
+parse_app_args(uint8_t max_ports, int argc, char *argv[])
+{
+	int option_index, opt;
+	char **argvopt = argv;
+	static struct option lgopts[] = { /* no long options */
+		{NULL, 0, 0, 0 }
+	};
+	progname = argv[0];
+
+	while ((opt = getopt_long(argc, argvopt, "n:f:p:", lgopts,
+			&option_index)) != EOF) {
+		switch (opt) {
+		case 'p':
+			if (parse_portmask(max_ports, optarg) != 0) {
+				usage();
+				return -1;
+			}
+			break;
+		case 'n':
+			if (parse_num_nodes(optarg) != 0) {
+				usage();
+				return -1;
+			}
+			break;
+		case 'f':
+			if (parse_num_flows(optarg) != 0) {
+				usage();
+				return -1;
+			}
+			break;
+		default:
+			printf("ERROR: Unknown option '%c'\n", opt);
+			usage();
+			return -1;
+		}
+	}
+
+	if (info->num_ports == 0 || num_nodes == 0) {
+		usage();
+		return -1;
+	}
+
+	if (info->num_ports % 2 != 0) {
+		printf("ERROR: application requires an even "
+				"number of ports to use\n");
+		return -1;
+	}
+	return 0;
+}
diff --git a/examples/flow_distributor/distributor/args.h b/examples/flow_distributor/distributor/args.h
new file mode 100644
index 0000000..cacf395
--- /dev/null
+++ b/examples/flow_distributor/distributor/args.h
@@ -0,0 +1,39 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ARGS_H_
+#define _ARGS_H_
+
+int parse_app_args(uint8_t max_ports, int argc, char *argv[]);
+
+#endif /* ifndef _ARGS_H_ */
diff --git a/examples/flow_distributor/distributor/init.c b/examples/flow_distributor/distributor/init.c
new file mode 100644
index 0000000..3b0aa85
--- /dev/null
+++ b/examples/flow_distributor/distributor/init.c
@@ -0,0 +1,371 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_byteorder.h>
+#include <rte_atomic.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_ring.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_memcpy.h>
+#include <rte_mbuf.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+#include <rte_cycles.h>
+#include <rte_efd.h>
+#include <rte_hash.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+#define MBUFS_PER_NODE 1536
+#define MBUFS_PER_PORT 1536
+#define MBUF_CACHE_SIZE 512
+
+#define RTE_MP_RX_DESC_DEFAULT 512
+#define RTE_MP_TX_DESC_DEFAULT 512
+#define NODE_QUEUE_RINGSIZE 128
+
+#define NO_FLAGS 0
+
+/* The mbuf pool for packet rx */
+struct rte_mempool *pktmbuf_pool;
+
+/* array of info/queues for nodes */
+struct node *nodes;
+
+/* Flow distributor table */
+struct rte_efd_table *efd_table;
+
+/* Shared info between distributor and nodes */
+struct shared_info *info;
+
+/**
+ * Initialise the mbuf pool for packet reception for the NIC, and any other
+ * buffer pools needed by the app - currently none.
+ */
+static int
+init_mbuf_pools(void)
+{
+	const unsigned int num_mbufs = (num_nodes * MBUFS_PER_NODE) +
+			(info->num_ports * MBUFS_PER_PORT);
+
+	/*
+	 * Don't pass single-producer/single-consumer flags to mbuf create as it
+	 * seems faster to use a cache instead
+	 */
+	printf("Creating mbuf pool '%s' [%u mbufs] ...\n",
+			PKTMBUF_POOL_NAME, num_mbufs);
+	pktmbuf_pool = rte_pktmbuf_pool_create(PKTMBUF_POOL_NAME, num_mbufs,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+	return pktmbuf_pool == NULL; /* 0  on success */
+}
+
+/**
+ * Initialise an individual port:
+ * - configure number of rx and tx rings
+ * - set up each rx ring, to pull from the main mbuf pool
+ * - set up each tx ring
+ * - start the port and report its status to stdout
+ */
+static int
+init_port(uint8_t port_num)
+{
+	/* for port configuration all features are off by default */
+	const struct rte_eth_conf port_conf = {
+		.rxmode = {
+			.mq_mode = ETH_MQ_RX_RSS
+		}
+	};
+	const uint16_t rx_rings = 1, tx_rings = num_nodes;
+	const uint16_t rx_ring_size = RTE_MP_RX_DESC_DEFAULT;
+	const uint16_t tx_ring_size = RTE_MP_TX_DESC_DEFAULT;
+
+	uint16_t q;
+	int retval;
+
+	printf("Port %u init ... ", (unsigned int)port_num);
+	fflush(stdout);
+
+	/*
+	 * Standard DPDK port initialisation - config port, then set up
+	 * rx and tx rings.
+	 */
+	retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size,
+				rte_eth_dev_socket_id(port_num),
+				NULL, pktmbuf_pool);
+		if (retval < 0)
+			return retval;
+	}
+
+	for (q = 0; q < tx_rings; q++) {
+		retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size,
+				rte_eth_dev_socket_id(port_num),
+				NULL);
+		if (retval < 0)
+			return retval;
+	}
+
+	rte_eth_promiscuous_enable(port_num);
+
+	retval = rte_eth_dev_start(port_num);
+	if (retval < 0)
+		return retval;
+
+	printf("done:\n");
+
+	return 0;
+}
+
+/**
+ * Set up the DPDK rings which will be used to pass packets, via
+ * pointers, between the multi-process distributor and node processes.
+ * Each node needs one RX queue.
+ */
+static int
+init_shm_rings(void)
+{
+	unsigned int i;
+	unsigned int socket_id;
+	const char *q_name;
+	const unsigned int ringsize = NODE_QUEUE_RINGSIZE;
+
+	nodes = rte_malloc("node details",
+		sizeof(*nodes) * num_nodes, 0);
+	if (nodes == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot allocate memory for "
+				"node program details\n");
+
+	for (i = 0; i < num_nodes; i++) {
+		/* Create an RX queue for each node */
+		socket_id = rte_socket_id();
+		q_name = get_rx_queue_name(i);
+		nodes[i].rx_q = rte_ring_create(q_name,
+				ringsize, socket_id,
+				RING_F_SP_ENQ | RING_F_SC_DEQ);
+		if (nodes[i].rx_q == NULL)
+			rte_exit(EXIT_FAILURE, "Cannot create rx ring queue "
+					"for node %u\n", i);
+	}
+	return 0;
+}
+
+/*
+ * Create flow distributor table which will contain all the flows
+ * that will be distributed among the nodes
+ */
+static void
+create_flow_distributor_table(void)
+{
+	uint8_t socket_id = rte_socket_id();
+
+	/* create table */
+	efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
+			1 << socket_id,	socket_id);
+
+	if (efd_table == NULL)
+		rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
+}
+
+static void
+populate_flow_distributor_table(void)
+{
+	unsigned int i;
+	int32_t ret;
+	uint32_t ip_dst;
+	uint8_t socket_id = rte_socket_id();
+	uint64_t node_id;
+
+	/* Add flows in table */
+	for (i = 0; i < num_flows; i++) {
+		node_id = i % num_nodes;
+
+		ip_dst = rte_cpu_to_be_32(i);
+		ret = rte_efd_update(efd_table, socket_id,
+				(void *)&ip_dst, (efd_value_t)node_id);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
+					"flow distributor table\n", i);
+	}
+
+	printf("EFD table: Adding 0x%x keys\n", num_flows);
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+	uint8_t portid, count, all_ports_up, print_flag = 0;
+	struct rte_eth_link link;
+
+	printf("\nChecking link status");
+	fflush(stdout);
+	for (count = 0; count <= MAX_CHECK_TIME; count++) {
+		all_ports_up = 1;
+		for (portid = 0; portid < port_num; portid++) {
+			if ((port_mask & (1 << info->id[portid])) == 0)
+				continue;
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(info->id[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", info->id[portid],
+						(unsigned int)link.link_speed,
+				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
+					("full-duplex") : ("half-duplex\n"));
+				else
+					printf("Port %d Link Down\n",
+						(uint8_t)info->id[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");
+		}
+	}
+}
+
+/**
+ * Main init function for the multi-process distributor app,
+ * calls subfunctions to do each stage of the initialisation.
+ */
+int
+init(int argc, char *argv[])
+{
+	int retval;
+	const struct rte_memzone *mz;
+	uint8_t i, total_ports;
+
+	/* init EAL, parsing EAL args */
+	retval = rte_eal_init(argc, argv);
+	if (retval < 0)
+		return -1;
+	argc -= retval;
+	argv += retval;
+
+	/* get total number of ports */
+	total_ports = rte_eth_dev_count();
+
+	/* set up array for port data */
+	mz = rte_memzone_reserve(MZ_SHARED_INFO, sizeof(*info),
+				rte_socket_id(), NO_FLAGS);
+	if (mz == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot reserve memory zone "
+				"for port information\n");
+	memset(mz->addr, 0, sizeof(*info));
+	info = mz->addr;
+
+	/* parse additional, application arguments */
+	retval = parse_app_args(total_ports, argc, argv);
+	if (retval != 0)
+		return -1;
+
+	/* initialise mbuf pools */
+	retval = init_mbuf_pools();
+	if (retval != 0)
+		rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n");
+
+	/* now initialise the ports we will use */
+	for (i = 0; i < info->num_ports; i++) {
+		retval = init_port(info->id[i]);
+		if (retval != 0)
+			rte_exit(EXIT_FAILURE, "Cannot initialise port %u\n",
+					(unsigned int) i);
+	}
+
+	check_all_ports_link_status(info->num_ports, (~0x0));
+
+	/* initialise the node queues/rings for inter-eu comms */
+	init_shm_rings();
+
+	/* Create the flow distributor table */
+	create_flow_distributor_table();
+
+	/* Populate the flow distributor table */
+	populate_flow_distributor_table();
+
+	/* Share the total number of nodes */
+	info->num_nodes = num_nodes;
+
+	/* Share the total number of flows */
+	info->num_flows = num_flows;
+	return 0;
+}
diff --git a/examples/flow_distributor/distributor/init.h b/examples/flow_distributor/distributor/init.h
new file mode 100644
index 0000000..d11aacf
--- /dev/null
+++ b/examples/flow_distributor/distributor/init.h
@@ -0,0 +1,76 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _INIT_H_
+#define _INIT_H_
+
+/*
+ * #include <rte_ring.h>
+ * #include "args.h"
+ */
+
+/*
+ * Define a node structure with all needed info, including
+ * stats from the nodes.
+ */
+struct node {
+	struct rte_ring *rx_q;
+	unsigned int node_id;
+	/* these stats hold how many packets the node will actually receive,
+	 * and how many packets were dropped because the node's queue was full.
+	 * The port-info stats, in contrast, record how many packets were received
+	 * or transmitted on an actual NIC port.
+	 */
+	struct {
+		uint64_t rx;
+		uint64_t rx_drop;
+	} stats;
+};
+
+extern struct rte_efd_table *efd_table;
+extern struct node *nodes;
+
+/*
+ * shared information between distributor and nodes: number of clients,
+ * port numbers, rx and tx stats etc.
+ */
+extern struct shared_info *info;
+
+extern struct rte_mempool *pktmbuf_pool;
+extern uint8_t num_nodes;
+extern unsigned int num_sockets;
+extern uint32_t num_flows;
+
+int init(int argc, char *argv[]);
+
+#endif /* ifndef _INIT_H_ */
diff --git a/examples/flow_distributor/distributor/main.c b/examples/flow_distributor/distributor/main.c
new file mode 100644
index 0000000..f97f003
--- /dev/null
+++ b/examples/flow_distributor/distributor/main.c
@@ -0,0 +1,362 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <inttypes.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <netinet/ip.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_byteorder.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_atomic.h>
+#include <rte_ring.h>
+#include <rte_log.h>
+#include <rte_debug.h>
+#include <rte_mempool.h>
+#include <rte_memcpy.h>
+#include <rte_mbuf.h>
+#include <rte_ether.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ethdev.h>
+#include <rte_byteorder.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+#include <rte_efd.h>
+#include <rte_ip.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+/*
+ * When doing reads from the NIC or the node queues,
+ * use this batch size
+ */
+#define PACKET_READ_SIZE 32
+
+/*
+ * Local buffers to put packets in, used to send packets in bursts to the
+ * nodes
+ */
+struct node_rx_buf {
+	struct rte_mbuf *buffer[PACKET_READ_SIZE];
+	uint16_t count;
+};
+
+struct flow_distributor_stats {
+	uint64_t distributed;
+	uint64_t drop;
+} flow_dist_stats;
+
+/* One buffer per node rx queue - dynamically allocate array */
+static struct node_rx_buf *cl_rx_buf;
+
+static const char *
+get_printable_mac_addr(uint8_t port)
+{
+	static const char err_address[] = "00:00:00:00:00:00";
+	static char addresses[RTE_MAX_ETHPORTS][sizeof(err_address)];
+	struct ether_addr mac;
+
+	if (unlikely(port >= RTE_MAX_ETHPORTS))
+		return err_address;
+	if (unlikely(addresses[port][0] == '\0')) {
+		rte_eth_macaddr_get(port, &mac);
+		snprintf(addresses[port], sizeof(addresses[port]),
+				"%02x:%02x:%02x:%02x:%02x:%02x\n",
+				mac.addr_bytes[0], mac.addr_bytes[1],
+				mac.addr_bytes[2], mac.addr_bytes[3],
+				mac.addr_bytes[4], mac.addr_bytes[5]);
+	}
+	return addresses[port];
+}
+
+/*
+ * This function displays the recorded statistics for each port
+ * and for each node. It uses ANSI terminal codes to clear
+ * screen when called. It is called from a single non-master
+ * thread in the distributor process, when the process is run with more
+ * than one lcore enabled.
+ */
+static void
+do_stats_display(void)
+{
+	unsigned int i, j;
+	const char clr[] = {27, '[', '2', 'J', '\0'};
+	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+	uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
+	uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
+
+	/* to get TX stats, we need to do some summing calculations */
+	memset(port_tx, 0, sizeof(port_tx));
+	memset(port_tx_drop, 0, sizeof(port_tx_drop));
+	memset(node_tx, 0, sizeof(node_tx));
+	memset(node_tx_drop, 0, sizeof(node_tx_drop));
+
+	for (i = 0; i < num_nodes; i++) {
+		const struct tx_stats *tx = &info->tx_stats[i];
+
+		for (j = 0; j < info->num_ports; j++) {
+			const uint64_t tx_val = tx->tx[info->id[j]];
+			const uint64_t drop_val = tx->tx_drop[info->id[j]];
+
+			port_tx[j] += tx_val;
+			port_tx_drop[j] += drop_val;
+			node_tx[i] += tx_val;
+			node_tx_drop[i] += drop_val;
+		}
+	}
+
+	/* Clear screen and move to top left */
+	printf("%s%s", clr, topLeft);
+
+	printf("PORTS\n");
+	printf("-----\n");
+	for (i = 0; i < info->num_ports; i++)
+		printf("Port %u: '%s'\t", (unsigned int)info->id[i],
+				get_printable_mac_addr(info->id[i]));
+	printf("\n\n");
+	for (i = 0; i < info->num_ports; i++) {
+		printf("Port %u - rx: %9"PRIu64"\t"
+				"tx: %9"PRIu64"\n",
+				(unsigned int)info->id[i], info->rx_stats.rx[i],
+				port_tx[i]);
+	}
+
+	printf("\nFLOW DISTRIBUTOR\n");
+	printf("-----\n");
+	printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
+			flow_dist_stats.distributed, flow_dist_stats.drop);
+
+	printf("\nNODES\n");
+	printf("-------\n");
+	for (i = 0; i < num_nodes; i++) {
+		const unsigned long long rx = nodes[i].stats.rx;
+		const unsigned long long rx_drop = nodes[i].stats.rx_drop;
+		const struct filter_stats *filter = &info->filter_stats[i];
+
+		printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
+				"            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
+				"            filter_passed: %9"PRIu64", "
+				"filter_drop: %9"PRIu64"\n",
+				i, rx, rx_drop, node_tx[i], node_tx_drop[i],
+				filter->passed, filter->drop);
+	}
+
+	printf("\n");
+}
+
+/*
+ * The function called from each non-master lcore used by the process.
+ * The test_and_set function is used to randomly pick a single lcore on which
+ * the code to display the statistics will run. Otherwise, the code just
+ * repeatedly sleeps.
+ */
+static int
+sleep_lcore(__attribute__((unused)) void *dummy)
+{
+	/* Used to pick a display thread - static, so zero-initialised */
+	static rte_atomic32_t display_stats;
+
+	/* Only one core should display stats */
+	if (rte_atomic32_test_and_set(&display_stats)) {
+		const unsigned int sleeptime = 1;
+
+		printf("Core %u displaying statistics\n", rte_lcore_id());
+
+		/* Longer initial pause so above printf is seen */
+		sleep(sleeptime * 3);
+
+		/* Loop forever: sleep always returns 0 or <= param */
+		while (sleep(sleeptime) <= sleeptime)
+			do_stats_display();
+	}
+	return 0;
+}
+
+/*
+ * Function to set all the node statistic values to zero.
+ * Called at program startup.
+ */
+static void
+clear_stats(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_nodes; i++)
+		nodes[i].stats.rx = nodes[i].stats.rx_drop = 0;
+}
+
+/*
+ * send a burst of traffic to a node, assuming there are packets
+ * available to be sent to this node
+ */
+static void
+flush_rx_queue(uint16_t node)
+{
+	uint16_t j;
+	struct node *cl;
+
+	if (cl_rx_buf[node].count == 0)
+		return;
+
+	cl = &nodes[node];
+	if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
+			cl_rx_buf[node].count) != 0){
+		for (j = 0; j < cl_rx_buf[node].count; j++)
+			rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
+		cl->stats.rx_drop += cl_rx_buf[node].count;
+	} else
+		cl->stats.rx += cl_rx_buf[node].count;
+
+	cl_rx_buf[node].count = 0;
+}
+
+/*
+ * marks a packet down to be sent to a particular node process
+ */
+static inline void
+enqueue_rx_packet(uint8_t node, struct rte_mbuf *buf)
+{
+	cl_rx_buf[node].buffer[cl_rx_buf[node].count++] = buf;
+}
+
+/*
+ * This function takes a group of packets and routes them
+ * individually to the node process. Very simply round-robins the packets
+ * without checking any of the packet contents.
+ */
+static void
+process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
+		uint16_t rx_count, unsigned int socket_id)
+{
+	uint16_t i;
+	uint8_t node;
+	efd_value_t data[RTE_EFD_BURST_MAX];
+	const void *key_ptrs[RTE_EFD_BURST_MAX];
+
+	struct ipv4_hdr *ipv4_hdr;
+	uint32_t ipv4_dst_ip[RTE_EFD_BURST_MAX];
+
+	for (i = 0; i < rx_count; i++) {
+		/* Handle IPv4 header.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
+				sizeof(struct ether_hdr));
+		ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+		key_ptrs[i] = (void *)&ipv4_dst_ip[i];
+	}
+
+	rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
+				(const void **) key_ptrs, data);
+	for (i = 0; i < rx_count; i++) {
+		node = (uint8_t) ((uintptr_t)data[i]);
+
+		if (node >= num_nodes) {
+			/*
+			 * Node is out of range, which means that
+			 * flow has not been inserted
+			 */
+			flow_dist_stats.drop++;
+			rte_pktmbuf_free(pkts[i]);
+		} else {
+			flow_dist_stats.distributed++;
+			enqueue_rx_packet(node, pkts[i]);
+		}
+	}
+
+	for (i = 0; i < num_nodes; i++)
+		flush_rx_queue(i);
+}
+
+/*
+ * Function called by the master lcore of the DPDK process.
+ */
+static void
+do_packet_forwarding(void)
+{
+	unsigned int port_num = 0; /* indexes the port[] array */
+	unsigned int socket_id = rte_socket_id();
+
+	for (;;) {
+		struct rte_mbuf *buf[PACKET_READ_SIZE];
+		uint16_t rx_count;
+
+		/* read a port */
+		rx_count = rte_eth_rx_burst(info->id[port_num], 0,
+				buf, PACKET_READ_SIZE);
+		info->rx_stats.rx[port_num] += rx_count;
+
+		/* Now process the NIC packets read */
+		if (likely(rx_count > 0))
+			process_packets(port_num, buf, rx_count, socket_id);
+
+		/* move to next port */
+		if (++port_num == info->num_ports)
+			port_num = 0;
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	/* initialise the system */
+	if (init(argc, argv) < 0)
+		return -1;
+	RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+	cl_rx_buf = calloc(num_nodes, sizeof(cl_rx_buf[0]));
+
+	/* clear statistics */
+	clear_stats();
+
+	/* put all other cores to sleep bar master */
+	rte_eal_mp_remote_launch(sleep_lcore, NULL, SKIP_MASTER);
+
+	do_packet_forwarding();
+	return 0;
+}
diff --git a/examples/flow_distributor/node/Makefile b/examples/flow_distributor/node/Makefile
new file mode 100644
index 0000000..8cf7b65
--- /dev/null
+++ b/examples/flow_distributor/node/Makefile
@@ -0,0 +1,48 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = node
+
+# all source are stored in SRCS-y
+SRCS-y := node.c
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/flow_distributor/node/node.c b/examples/flow_distributor/node/node.c
new file mode 100644
index 0000000..1f1e7e7
--- /dev/null
+++ b/examples/flow_distributor/node/node.c
@@ -0,0 +1,417 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_atomic.h>
+#include <rte_branch_prediction.h>
+#include <rte_log.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_ring.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_string_fns.h>
+#include <rte_ip.h>
+
+#include "common.h"
+
+/* Number of packets to attempt to read from queue */
+#define PKT_READ_SIZE  ((uint16_t)32)
+
+/*
+ * Our node id number - tells us which rx queue to read, and NIC TX
+ * queue to write to.
+ */
+static uint8_t node_id;
+
+#define MBQ_CAPACITY 32
+
+/* maps input ports to output ports for packets */
+static uint8_t output_ports[RTE_MAX_ETHPORTS];
+
+/* buffers up a set of packet that are ready to send */
+struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
+
+/* shared data from distributor. We update statistics here */
+static struct tx_stats *tx_stats;
+
+static struct filter_stats *filter_stats;
+
+/*
+ * print a usage message
+ */
+static void
+usage(const char *progname)
+{
+	printf("Usage: %s [EAL args] -- -n <node_id>\n\n", progname);
+}
+
+/*
+ * Convert the node id number from a string to an int.
+ */
+static int
+parse_node_num(const char *node)
+{
+	char *end = NULL;
+	unsigned long temp;
+
+	if (node == NULL || *node == '\0')
+		return -1;
+
+	temp = strtoul(node, &end, 10);
+	if (end == NULL || *end != '\0')
+		return -1;
+
+	node_id = (uint8_t)temp;
+	return 0;
+}
+
+/*
+ * Parse the application arguments to the node app.
+ */
+static int
+parse_app_args(int argc, char *argv[])
+{
+	int option_index, opt;
+	char **argvopt = argv;
+	const char *progname = NULL;
+	static struct option lgopts[] = { /* no long options */
+		{NULL, 0, 0, 0 }
+	};
+	progname = argv[0];
+
+	while ((opt = getopt_long(argc, argvopt, "n:", lgopts,
+		&option_index)) != EOF) {
+		switch (opt) {
+		case 'n':
+			if (parse_node_num(optarg) != 0) {
+				usage(progname);
+				return -1;
+			}
+			break;
+		default:
+			usage(progname);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Tx buffer error callback
+ */
+static void
+flush_tx_error_callback(struct rte_mbuf **unsent, uint16_t count,
+		void *userdata) {
+	int i;
+	uint8_t port_id = (uintptr_t)userdata;
+
+	tx_stats->tx_drop[port_id] += count;
+
+	/* free the mbufs which failed from transmit */
+	for (i = 0; i < count; i++)
+		rte_pktmbuf_free(unsent[i]);
+
+}
+
+static void
+configure_tx_buffer(uint8_t port_id, uint16_t size)
+{
+	int ret;
+
+	/* Initialize TX buffers */
+	tx_buffer[port_id] = rte_zmalloc_socket("tx_buffer",
+			RTE_ETH_TX_BUFFER_SIZE(size), 0,
+			rte_eth_dev_socket_id(port_id));
+	if (tx_buffer[port_id] == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx "
+				"on port %u\n", (unsigned int) port_id);
+
+	rte_eth_tx_buffer_init(tx_buffer[port_id], size);
+
+	ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[port_id],
+			flush_tx_error_callback, (void *)(intptr_t)port_id);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Cannot set error callback for "
+			"tx buffer on port %u\n", (unsigned int) port_id);
+}
+
+/*
+ * set up output ports so that all traffic on port gets sent out
+ * its paired port. Index using actual port numbers since that is
+ * what comes in the mbuf structure.
+ */
+static void
+configure_output_ports(const struct shared_info *info)
+{
+	int i;
+
+	if (info->num_ports > RTE_MAX_ETHPORTS)
+		rte_exit(EXIT_FAILURE, "Too many ethernet ports. "
+				"RTE_MAX_ETHPORTS = %u\n",
+				(unsigned int)RTE_MAX_ETHPORTS);
+	for (i = 0; i < info->num_ports - 1; i += 2) {
+		uint8_t p1 = info->id[i];
+		uint8_t p2 = info->id[i+1];
+
+		output_ports[p1] = p2;
+		output_ports[p2] = p1;
+
+		configure_tx_buffer(p1, MBQ_CAPACITY);
+		configure_tx_buffer(p2, MBQ_CAPACITY);
+
+	}
+}
+
+/*
+ * Create the hash table that will contain the flows that
+ * the node will handle, which will be used to decide if packet
+ * is transmitted or dropped.
+ */
+static struct rte_hash *
+create_hash_table(const struct shared_info *info)
+{
+	uint32_t num_flows_node = info->num_flows / info->num_nodes;
+	char name[RTE_HASH_NAMESIZE];
+	struct rte_hash *h;
+
+	/* create table */
+	struct rte_hash_parameters hash_params = {
+		.entries = num_flows_node * 2, /* table load = 50% */
+		.key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
+		.socket_id = rte_socket_id(),
+		.hash_func_init_val = 0,
+	};
+
+	snprintf(name, sizeof(name), "hash_table_%d", node_id);
+	hash_params.name = name;
+	h = rte_hash_create(&hash_params);
+
+	if (h == NULL)
+		rte_exit(EXIT_FAILURE,
+				"Problem creating the hash table for node %d\n",
+				node_id);
+	return h;
+}
+
+static void
+populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
+{
+	unsigned int i;
+	int32_t ret;
+	uint32_t ip_dst;
+	uint32_t num_flows_node = 0;
+	uint64_t target_node;
+
+	/* Add flows in table */
+	for (i = 0; i < info->num_flows; i++) {
+		target_node = i % info->num_nodes;
+		if (target_node != node_id)
+			continue;
+
+		ip_dst = rte_cpu_to_be_32(i);
+
+		ret = rte_hash_add_key(h, (void *) &ip_dst);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "Unable to add entry %u "
+					"in hash table\n", i);
+		else
+			num_flows_node++;
+
+	}
+
+	printf("Hash table: Adding 0x%x keys\n", num_flows_node);
+}
+
+/*
+ * This function performs routing of packets
+ * Just sends each input packet out an output port based solely on the input
+ * port it arrived on.
+ */
+static inline void
+transmit_packet(struct rte_mbuf *buf)
+{
+	int sent;
+	const uint8_t in_port = buf->port;
+	const uint8_t out_port = output_ports[in_port];
+	struct rte_eth_dev_tx_buffer *buffer = tx_buffer[out_port];
+
+	sent = rte_eth_tx_buffer(out_port, node_id, buffer, buf);
+	if (sent)
+		tx_stats->tx[out_port] += sent;
+
+}
+
+static inline void
+handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
+{
+	struct ipv4_hdr *ipv4_hdr;
+	uint32_t ipv4_dst_ip[PKT_READ_SIZE];
+	const void *key_ptrs[PKT_READ_SIZE];
+	unsigned int i;
+	int32_t positions[PKT_READ_SIZE] = {0};
+
+	for (i = 0; i < num_packets; i++) {
+		/* Handle IPv4 header.*/
+		ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
+				sizeof(struct ether_hdr));
+		ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+		key_ptrs[i] = &ipv4_dst_ip[i];
+	}
+	/* Check if packets belongs to any flows handled by this node */
+	rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
+
+	for (i = 0; i < num_packets; i++) {
+		if (likely(positions[i] >= 0)) {
+			filter_stats->passed++;
+			transmit_packet(bufs[i]);
+		} else {
+			filter_stats->drop++;
+			/* Drop packet, as flow is not handled by this node */
+			rte_pktmbuf_free(bufs[i]);
+		}
+	}
+}
+
+/*
+ * Application main function - loops through
+ * receiving and processing packets. Never returns
+ */
+int
+main(int argc, char *argv[])
+{
+	const struct rte_memzone *mz;
+	struct rte_ring *rx_ring;
+	struct rte_hash *h;
+	struct rte_mempool *mp;
+	struct shared_info *info;
+	int need_flush = 0; /* indicates whether we have unsent packets */
+	int retval;
+	void *pkts[PKT_READ_SIZE];
+	uint16_t sent;
+
+	retval = rte_eal_init(argc, argv);
+	if (retval  < 0)
+		return -1;
+	argc -= retval;
+	argv += retval;
+
+	if (parse_app_args(argc, argv) < 0)
+		rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n");
+
+	if (rte_eth_dev_count() == 0)
+		rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
+
+	rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
+	if (rx_ring == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot get RX ring - "
+				"is distributor process running?\n");
+
+	mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+	if (mp == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
+
+	mz = rte_memzone_lookup(MZ_SHARED_INFO);
+	if (mz == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
+	info = mz->addr;
+	tx_stats = &(info->tx_stats[node_id]);
+	filter_stats = &(info->filter_stats[node_id]);
+
+	configure_output_ports(info);
+
+	h = create_hash_table(info);
+
+	populate_hash_table(h, info);
+
+	RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+	printf("\nNode process %d handling packets\n", node_id);
+	printf("[Press Ctrl-C to quit ...]\n");
+
+	for (;;) {
+		uint16_t  rx_pkts = PKT_READ_SIZE;
+		uint8_t port;
+
+		/*
+		 * Try dequeuing max possible packets first, if that fails,
+		 * get the most we can. Loop body should only execute once,
+		 * maximum
+		 */
+		while (rx_pkts > 0 &&
+				unlikely(rte_ring_dequeue_bulk(rx_ring, pkts,
+					rx_pkts) != 0))
+			rx_pkts = (uint16_t)RTE_MIN(rte_ring_count(rx_ring),
+					PKT_READ_SIZE);
+
+		if (unlikely(rx_pkts == 0)) {
+			if (need_flush)
+				for (port = 0; port < info->num_ports; port++) {
+					sent = rte_eth_tx_buffer_flush(
+							info->id[port],
+							node_id,
+							tx_buffer[port]);
+					if (unlikely(sent))
+						tx_stats->tx[port] += sent;
+				}
+			need_flush = 0;
+			continue;
+		}
+
+		handle_packets(h, (struct rte_mbuf **)pkts, rx_pkts);
+
+		need_flush = 1;
+	}
+}
diff --git a/examples/flow_distributor/shared/common.h b/examples/flow_distributor/shared/common.h
new file mode 100644
index 0000000..5dcffd6
--- /dev/null
+++ b/examples/flow_distributor/shared/common.h
@@ -0,0 +1,99 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <rte_hash_crc.h>
+#include <rte_hash.h>
+
+#define MAX_NODES             16
+/*
+ * Shared port info, including statistics information for display by distributor.
+ * Structure will be put in a memzone.
+ * - All port id values share one cache line as this data will be read-only
+ * during operation.
+ * - All rx statistic values share cache lines, as this data is written only
+ * by the distributor process. (rare reads by stats display)
+ * - The tx statistics have values for all ports per cache line, but the stats
+ * themselves are written by the nodes, so we have a distinct set, on different
+ * cache lines for each node to use.
+ */
+struct rx_stats {
+	uint64_t rx[RTE_MAX_ETHPORTS];
+} __rte_cache_aligned;
+
+struct tx_stats {
+	uint64_t tx[RTE_MAX_ETHPORTS];
+	uint64_t tx_drop[RTE_MAX_ETHPORTS];
+} __rte_cache_aligned;
+
+struct filter_stats {
+	uint64_t drop;
+	uint64_t passed;
+} __rte_cache_aligned;
+
+struct shared_info {
+	uint8_t num_nodes;
+	uint8_t num_ports;
+	uint32_t num_flows;
+	uint8_t id[RTE_MAX_ETHPORTS];
+	struct rx_stats rx_stats;
+	struct tx_stats tx_stats[MAX_NODES];
+	struct filter_stats filter_stats[MAX_NODES];
+};
+
+/* define common names for structures shared between distributor and node */
+#define MP_NODE_RXQ_NAME "MProc_Node_%u_RX"
+#define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool"
+#define MZ_SHARED_INFO "MProc_shared_info"
+
+/*
+ * Given the rx queue name template above, get the queue name
+ */
+static inline const char *
+get_rx_queue_name(unsigned int id)
+{
+	/*
+	 * Buffer for return value. Size calculated by %u being replaced
+	 * by maximum 3 digits (plus an extra byte for safety)
+	 */
+	static char buffer[sizeof(MP_NODE_RXQ_NAME) + 2];
+
+	snprintf(buffer, sizeof(buffer) - 1, MP_NODE_RXQ_NAME, id);
+	return buffer;
+}
+
+#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
+
+#endif
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 2/5] app/test: add EFD functional and perf tests
From: Pablo de Lara @ 2017-01-15 12:04 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara, Byron Marohn, Karla Saur, Saikrishna Edupuganti
In-Reply-To: <1484481875-126335-1-git-send-email-pablo.de.lara.guarch@intel.com>

Signed-off-by: Byron Marohn <byron.marohn@intel.com>
Signed-off-by: Karla Saur <karla.saur@intel.com>
Signed-off-by: Saikrishna Edupuganti <saikrishna.edupuganti@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Acked-by: Christian Maciocco <christian.maciocco@intel.com>
---
 MAINTAINERS              |   1 +
 app/test/Makefile        |   5 +-
 app/test/test_efd.c      | 494 +++++++++++++++++++++++++++++++++++++++++++++++
 app/test/test_efd_perf.c | 407 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 906 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_efd.c
 create mode 100644 app/test/test_efd_perf.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9c60d67..d812962 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -532,6 +532,7 @@ EFD
 M: Byron Marohn <byron.marohn@intel.com>
 M: Pablo de Lara Guarch <pablo.de.lara.guarch@intel.com>
 F: lib/librte_efd/
+F: app/test/test_efd*
 
 Hashes
 M: Bruce Richardson <bruce.richardson@intel.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 5be023a..9de301f 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -1,6 +1,6 @@
 #   BSD LICENSE
 #
-#   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+#   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
 #   All rights reserved.
 #
 #   Redistribution and use in source and binary forms, with or without
@@ -123,6 +123,9 @@ SRCS-y += test_logs.c
 SRCS-y += test_memcpy.c
 SRCS-y += test_memcpy_perf.c
 
+SRCS-$(CONFIG_RTE_LIBRTE_EFD) += test_efd.c
+SRCS-$(CONFIG_RTE_LIBRTE_EFD) += test_efd_perf.c
+
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_thash.c
 SRCS-$(CONFIG_RTE_LIBRTE_HASH) += test_hash_perf.c
diff --git a/app/test/test_efd.c b/app/test/test_efd.c
new file mode 100644
index 0000000..d5c3bd9
--- /dev/null
+++ b/app/test/test_efd.c
@@ -0,0 +1,494 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_memcpy.h>
+#include <rte_malloc.h>
+#include <rte_efd.h>
+#include <rte_byteorder.h>
+#include <rte_random.h>
+#include <rte_debug.h>
+#include <rte_ip.h>
+
+#include "test.h"
+
+#define EFD_TEST_KEY_LEN 8
+#define TABLE_SIZE (1 << 21)
+#define ITERATIONS 3
+static unsigned int test_socket_id;
+
+/* 5-tuple key type */
+struct flow_key {
+	uint32_t ip_src;
+	uint32_t ip_dst;
+	uint16_t port_src;
+	uint16_t port_dst;
+	uint8_t proto;
+} __attribute__((packed));
+/*
+ * Print out result of unit test efd operation.
+ */
+#if defined(UNIT_TEST_EFD_VERBOSE)
+
+static void print_key_info(const char *msg, const struct flow_key *key,
+		efd_value_t val)
+{
+	const uint8_t *p = (const uint8_t *) key;
+	unsigned int i;
+
+	printf("%s key:0x", msg);
+	for (i = 0; i < sizeof(struct flow_key); i++)
+		printf("%02X", p[i]);
+
+	printf(" @ val %d\n", val);
+}
+#else
+
+static void print_key_info(__attribute__((unused)) const char *msg,
+		__attribute__((unused)) const struct flow_key *key,
+		__attribute__((unused)) efd_value_t val)
+{
+}
+#endif
+
+/* Keys used by unit test functions */
+static struct flow_key keys[5] = {
+	{
+		.ip_src = IPv4(0x03, 0x02, 0x01, 0x00),
+		.ip_dst = IPv4(0x07, 0x06, 0x05, 0x04),
+		.port_src = 0x0908,
+		.port_dst = 0x0b0a,
+		.proto = 0x0c,
+	},
+	{
+		.ip_src = IPv4(0x13, 0x12, 0x11, 0x10),
+		.ip_dst = IPv4(0x17, 0x16, 0x15, 0x14),
+		.port_src = 0x1918,
+		.port_dst = 0x1b1a,
+		.proto = 0x1c,
+	},
+	{
+		.ip_src = IPv4(0x23, 0x22, 0x21, 0x20),
+		.ip_dst = IPv4(0x27, 0x26, 0x25, 0x24),
+		.port_src = 0x2928,
+		.port_dst = 0x2b2a,
+		.proto = 0x2c,
+	},
+	{
+		.ip_src = IPv4(0x33, 0x32, 0x31, 0x30),
+		.ip_dst = IPv4(0x37, 0x36, 0x35, 0x34),
+		.port_src = 0x3938,
+		.port_dst = 0x3b3a,
+		.proto = 0x3c,
+	},
+	{
+		.ip_src = IPv4(0x43, 0x42, 0x41, 0x40),
+		.ip_dst = IPv4(0x47, 0x46, 0x45, 0x44),
+		.port_src = 0x4948,
+		.port_dst = 0x4b4a,
+		.proto = 0x4c,
+	}
+};
+/* Array to store the data */
+efd_value_t data[5];
+
+static inline uint8_t efd_get_all_sockets_bitmask(void)
+{
+	uint8_t all_cpu_sockets_bitmask = 0;
+	unsigned int i;
+	unsigned int next_lcore = rte_get_master_lcore();
+	const int val_true = 1, val_false = 0;
+	for (i = 0; i < rte_lcore_count(); i++) {
+		all_cpu_sockets_bitmask |= 1 << rte_lcore_to_socket_id(next_lcore);
+		next_lcore = rte_get_next_lcore(next_lcore, val_false, val_true);
+	}
+
+	return all_cpu_sockets_bitmask;
+}
+
+/*
+ * Basic sequence of operations for a single key:
+ *      - add
+ *      - lookup (hit)
+ *      - delete
+ * Note: lookup (miss) is not applicable since this is a filter
+ */
+static int test_add_delete(void)
+{
+	struct rte_efd_table *handle;
+	/* test with standard add/lookup/delete functions */
+	efd_value_t prev_value;
+	printf("Entering %s\n", __func__);
+
+	handle = rte_efd_create("test_add_delete",
+			TABLE_SIZE, sizeof(struct flow_key),
+			efd_get_all_sockets_bitmask(), test_socket_id);
+	TEST_ASSERT_NOT_NULL(handle, "Error creating the EFD table\n");
+
+	data[0] = mrand48() & ((1 << RTE_EFD_VALUE_NUM_BITS) - 1);
+	TEST_ASSERT_SUCCESS(rte_efd_update(handle, test_socket_id, &keys[0],
+			data[0]),
+			"Error inserting the key");
+	print_key_info("Add", &keys[0], data[0]);
+
+	TEST_ASSERT_EQUAL(rte_efd_lookup(handle, test_socket_id, &keys[0]),
+			data[0],
+			"failed to find key");
+
+	TEST_ASSERT_SUCCESS(rte_efd_delete(handle, test_socket_id, &keys[0],
+			&prev_value),
+			"failed to delete key");
+	TEST_ASSERT_EQUAL(prev_value, data[0],
+			"failed to delete the expected value, got %d, "
+			"expected %d", prev_value, data[0]);
+	print_key_info("Del", &keys[0], data[0]);
+
+	rte_efd_free(handle);
+
+	return 0;
+}
+
+/*
+ * Sequence of operations for a single key:
+ *      - add
+ *      - lookup: hit
+ *      - add: update
+ *      - lookup: hit (updated data)
+ *      - delete: hit
+ */
+static int test_add_update_delete(void)
+{
+	struct rte_efd_table *handle;
+	printf("Entering %s\n", __func__);
+	/* test with standard add/lookup/delete functions */
+	efd_value_t prev_value;
+	data[1] = mrand48() & ((1 << RTE_EFD_VALUE_NUM_BITS) - 1);
+
+	handle = rte_efd_create("test_add_update_delete", TABLE_SIZE,
+			sizeof(struct flow_key),
+			efd_get_all_sockets_bitmask(), test_socket_id);
+	TEST_ASSERT_NOT_NULL(handle, "Error creating the efd table\n");
+
+	TEST_ASSERT_SUCCESS(rte_efd_update(handle, test_socket_id, &keys[1],
+			data[1]), "Error inserting the key");
+	print_key_info("Add", &keys[1], data[1]);
+
+	TEST_ASSERT_EQUAL(rte_efd_lookup(handle, test_socket_id, &keys[1]),
+			data[1], "failed to find key");
+	print_key_info("Lkp", &keys[1], data[1]);
+
+	data[1] = data[1] + 1;
+	TEST_ASSERT_SUCCESS(rte_efd_update(handle, test_socket_id, &keys[1],
+			data[1]), "Error re-inserting the key");
+	print_key_info("Add", &keys[1], data[1]);
+
+	TEST_ASSERT_EQUAL(rte_efd_lookup(handle, test_socket_id, &keys[1]),
+			data[1], "failed to find key");
+	print_key_info("Lkp", &keys[1], data[1]);
+
+	TEST_ASSERT_SUCCESS(rte_efd_delete(handle, test_socket_id, &keys[1],
+			&prev_value), "failed to delete key");
+	TEST_ASSERT_EQUAL(prev_value, data[1],
+			"failed to delete the expected value, got %d, "
+			"expected %d", prev_value, data[1]);
+	print_key_info("Del", &keys[1], data[1]);
+
+
+	rte_efd_free(handle);
+	return 0;
+}
+
+/*
+ * Sequence of operations for find existing EFD table
+ *
+ *  - create table
+ *  - find existing table: hit
+ *  - find non-existing table: miss
+ *
+ */
+static int test_efd_find_existing(void)
+{
+	struct rte_efd_table *handle = NULL, *result = NULL;
+
+	printf("Entering %s\n", __func__);
+
+	/* Create EFD table. */
+	handle = rte_efd_create("efd_find_existing", TABLE_SIZE,
+			sizeof(struct flow_key),
+			efd_get_all_sockets_bitmask(), test_socket_id);
+	TEST_ASSERT_NOT_NULL(handle, "Error creating the efd table\n");
+
+	/* Try to find existing EFD table */
+	result = rte_efd_find_existing("efd_find_existing");
+	TEST_ASSERT_EQUAL(result, handle, "could not find existing efd table");
+
+	/* Try to find non-existing EFD table */
+	result = rte_efd_find_existing("efd_find_non_existing");
+	TEST_ASSERT_NULL(result, "found table that shouldn't exist");
+
+	/* Cleanup. */
+	rte_efd_free(handle);
+
+	return 0;
+}
+
+/*
+ * Sequence of operations for 5 keys
+ *      - add keys
+ *      - lookup keys: hit  (bulk)
+ *      - add keys (update)
+ *      - lookup keys: hit (updated data)
+ *      - delete keys : hit
+ */
+static int test_five_keys(void)
+{
+	struct rte_efd_table *handle;
+	const void *key_array[5] = {0};
+	efd_value_t result[5] = {0};
+	efd_value_t prev_value;
+	unsigned int i;
+	printf("Entering %s\n", __func__);
+
+	handle = rte_efd_create("test_five_keys", TABLE_SIZE,
+			sizeof(struct flow_key),
+			efd_get_all_sockets_bitmask(), test_socket_id);
+	TEST_ASSERT_NOT_NULL(handle, "Error creating the efd table\n");
+
+	/* Setup data */
+	for (i = 0; i < 5; i++)
+		data[i] = mrand48() & ((1 << RTE_EFD_VALUE_NUM_BITS) - 1);
+
+	/* Add */
+	for (i = 0; i < 5; i++) {
+		TEST_ASSERT_SUCCESS(rte_efd_update(handle, test_socket_id,
+				&keys[i], data[i]),
+				"Error inserting the key");
+		print_key_info("Add", &keys[i], data[i]);
+	}
+
+	/* Lookup */
+	for (i = 0; i < 5; i++)
+		key_array[i] = &keys[i];
+
+	rte_efd_lookup_bulk(handle, test_socket_id, 5,
+			(const void **) (void *) &key_array, result);
+
+	for (i = 0; i < 5; i++) {
+		TEST_ASSERT_EQUAL(result[i], data[i],
+				"bulk: failed to find key. Expected %d, got %d",
+				data[i], result[i]);
+		print_key_info("Lkp", &keys[i], data[i]);
+	}
+
+	/* Modify data (bulk) */
+	for (i = 0; i < 5; i++)
+		data[i] = data[i] + 1;
+
+	/* Add - update */
+	for (i = 0; i < 5; i++) {
+		TEST_ASSERT_SUCCESS(rte_efd_update(handle, test_socket_id,
+				&keys[i], data[i]),
+				"Error inserting the key");
+		print_key_info("Add", &keys[i], data[i]);
+	}
+
+	/* Lookup */
+	for (i = 0; i < 5; i++) {
+		TEST_ASSERT_EQUAL(rte_efd_lookup(handle, test_socket_id,
+				&keys[i]), data[i],
+				"failed to find key");
+		print_key_info("Lkp", &keys[i], data[i]);
+	}
+
+	/* Delete */
+	for (i = 0; i < 5; i++) {
+		TEST_ASSERT_SUCCESS(rte_efd_delete(handle, test_socket_id,
+				&keys[i], &prev_value),
+				"failed to delete key");
+		TEST_ASSERT_EQUAL(prev_value, data[i],
+				"failed to delete the expected value, got %d, "
+				"expected %d", prev_value, data[i]);
+		print_key_info("Del", &keys[i], data[i]);
+	}
+
+
+	rte_efd_free(handle);
+
+	return 0;
+}
+
+/*
+ * Test to see the average table utilization (entries added/max entries)
+ * before hitting a random entry that cannot be added
+ */
+static int test_average_table_utilization(void)
+{
+	struct rte_efd_table *handle = NULL;
+	uint32_t num_rules_in = TABLE_SIZE;
+	uint8_t simple_key[EFD_TEST_KEY_LEN];
+	unsigned int i, j;
+	unsigned int added_keys, average_keys_added = 0;
+
+	printf("Evaluating table utilization and correctness, please wait\n");
+	fflush(stdout);
+
+	for (j = 0; j < ITERATIONS; j++) {
+		handle = rte_efd_create("test_efd", num_rules_in,
+				EFD_TEST_KEY_LEN, efd_get_all_sockets_bitmask(),
+				test_socket_id);
+		if (handle == NULL) {
+			printf("efd table creation failed\n");
+			return -1;
+		}
+
+		unsigned int succeeded = 0;
+		unsigned int lost_keys = 0;
+
+		/* Add random entries until key cannot be added */
+		for (added_keys = 0; added_keys < num_rules_in; added_keys++) {
+
+			for (i = 0; i < EFD_TEST_KEY_LEN; i++)
+				simple_key[i] = rte_rand() & 0xFF;
+
+			efd_value_t val = simple_key[0];
+
+			if (rte_efd_update(handle, test_socket_id, simple_key,
+						val))
+				break; /* continue;*/
+			if (rte_efd_lookup(handle, test_socket_id, simple_key)
+					!= val)
+				lost_keys++;
+			else
+				succeeded++;
+		}
+
+		average_keys_added += succeeded;
+
+		/* Reset the table */
+		rte_efd_free(handle);
+
+		/* Print progress on operations */
+		printf("Added %10u	Succeeded %10u	Lost %10u\n",
+				added_keys, succeeded, lost_keys);
+		fflush(stdout);
+	}
+
+	average_keys_added /= ITERATIONS;
+
+	printf("\nAverage table utilization = %.2f%% (%u/%u)\n",
+			((double) average_keys_added / num_rules_in * 100),
+			average_keys_added, num_rules_in);
+
+	return 0;
+}
+
+/*
+ * Do tests for EFD creation with bad parameters.
+ */
+static int test_efd_creation_with_bad_parameters(void)
+{
+	struct rte_efd_table *handle, *tmp;
+	printf("Entering %s, **Errors are expected **\n", __func__);
+
+	handle = rte_efd_create("creation_with_bad_parameters_0", TABLE_SIZE, 0,
+			efd_get_all_sockets_bitmask(), test_socket_id);
+	if (handle != NULL) {
+		rte_efd_free(handle);
+		printf("Impossible creating EFD table successfully "
+			"if key_len in parameter is zero\n");
+		return -1;
+	}
+
+	handle = rte_efd_create("creation_with_bad_parameters_1", TABLE_SIZE,
+			sizeof(struct flow_key), 0, test_socket_id);
+	if (handle != NULL) {
+		rte_efd_free(handle);
+		printf("Impossible creating EFD table successfully "
+			"with invalid socket bitmask\n");
+		return -1;
+	}
+
+	handle = rte_efd_create("creation_with_bad_parameters_2", TABLE_SIZE,
+			sizeof(struct flow_key), efd_get_all_sockets_bitmask(),
+			255);
+	if (handle != NULL) {
+		rte_efd_free(handle);
+		printf("Impossible creating EFD table successfully "
+			"with invalid socket\n");
+		return -1;
+	}
+
+	/* test with same name should fail */
+	handle = rte_efd_create("same_name", TABLE_SIZE,
+			sizeof(struct flow_key),
+			efd_get_all_sockets_bitmask(), 0);
+	if (handle == NULL) {
+		printf("Cannot create first EFD table with 'same_name'\n");
+		return -1;
+	}
+	tmp = rte_efd_create("same_name", TABLE_SIZE, sizeof(struct flow_key),
+			efd_get_all_sockets_bitmask(), 0);
+	if (tmp != NULL) {
+		printf("Creation of EFD table with same name should fail\n");
+		rte_efd_free(handle);
+		rte_efd_free(tmp);
+		return -1;
+	}
+	rte_efd_free(handle);
+
+	printf("# Test successful. No more errors expected\n");
+
+	return 0;
+}
+
+static int
+test_efd(void)
+{
+
+	/* Unit tests */
+	if (test_add_delete() < 0)
+		return -1;
+	if (test_efd_find_existing() < 0)
+		return -1;
+	if (test_add_update_delete() < 0)
+		return -1;
+	if (test_five_keys() < 0)
+		return -1;
+	if (test_efd_creation_with_bad_parameters() < 0)
+		return -1;
+	if (test_average_table_utilization() < 0)
+		return -1;
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(efd_autotest, test_efd);
diff --git a/app/test/test_efd_perf.c b/app/test/test_efd_perf.c
new file mode 100644
index 0000000..998a25b
--- /dev/null
+++ b/app/test/test_efd_perf.c
@@ -0,0 +1,407 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <rte_lcore.h>
+#include <rte_cycles.h>
+#include <rte_malloc.h>
+#include <rte_random.h>
+#include <rte_efd.h>
+#include <rte_memcpy.h>
+#include <rte_thash.h>
+
+#include "test.h"
+#define NUM_KEYSIZES 10
+#define NUM_SHUFFLES 10
+#define MAX_KEYSIZE 64
+#define MAX_ENTRIES (1 << 19)
+#define KEYS_TO_ADD (MAX_ENTRIES * 3 / 4) /* 75% table utilization */
+#define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */
+static unsigned int test_socket_id;
+
+static inline uint8_t efd_get_all_sockets_bitmask(void)
+{
+	uint8_t all_cpu_sockets_bitmask = 0;
+	unsigned int i;
+	unsigned int next_lcore = rte_get_master_lcore();
+	const int val_true = 1, val_false = 0;
+	for (i = 0; i < rte_lcore_count(); i++) {
+		all_cpu_sockets_bitmask |= 1 << rte_lcore_to_socket_id(next_lcore);
+		next_lcore = rte_get_next_lcore(next_lcore, val_false, val_true);
+	}
+
+	return all_cpu_sockets_bitmask;
+}
+
+enum operations {
+	ADD = 0,
+	LOOKUP,
+	LOOKUP_MULTI,
+	DELETE,
+	NUM_OPERATIONS
+};
+
+struct efd_perf_params {
+	struct rte_efd_table *efd_table;
+	uint32_t key_size;
+	unsigned int cycle;
+};
+
+static uint32_t hashtest_key_lens[] = {
+	/* standard key sizes */
+	4, 8, 16, 32, 48, 64,
+	/* IPv4 SRC + DST + protocol, unpadded */
+	9,
+	/* IPv4 5-tuple, unpadded */
+	13,
+	/* IPv6 5-tuple, unpadded */
+	37,
+	/* IPv6 5-tuple, padded to 8-byte boundary */
+	40
+};
+
+/* Array to store number of cycles per operation */
+uint64_t cycles[NUM_KEYSIZES][NUM_OPERATIONS];
+
+/* Array to store the data */
+efd_value_t data[KEYS_TO_ADD];
+
+/* Array to store all input keys */
+uint8_t keys[KEYS_TO_ADD][MAX_KEYSIZE];
+
+/* Shuffle the keys that have been added, so lookups will be totally random */
+static void
+shuffle_input_keys(struct efd_perf_params *params)
+{
+	efd_value_t temp_data;
+	unsigned int i;
+	uint32_t swap_idx;
+	uint8_t temp_key[MAX_KEYSIZE];
+
+	for (i = KEYS_TO_ADD - 1; i > 0; i--) {
+		swap_idx = rte_rand() % i;
+
+		memcpy(temp_key, keys[i], hashtest_key_lens[params->cycle]);
+		temp_data = data[i];
+
+		memcpy(keys[i], keys[swap_idx], hashtest_key_lens[params->cycle]);
+		data[i] = data[swap_idx];
+
+		memcpy(keys[swap_idx], temp_key, hashtest_key_lens[params->cycle]);
+		data[swap_idx] = temp_data;
+	}
+}
+
+static int key_compare(const void *key1, const void *key2)
+{
+	return memcmp(key1, key2, MAX_KEYSIZE);
+}
+
+/*
+ * TODO: we could "error proof" these as done in test_hash_perf.c ln 165:
+ *
+ * The current setup may give errors if too full in some cases which we check
+ * for. However, since EFD allows for ~99% capacity, these errors are rare for
+ * #"KEYS_TO_ADD" which is 75% capacity.
+ */
+static int
+setup_keys_and_data(struct efd_perf_params *params, unsigned int cycle)
+{
+	unsigned int i, j;
+	int num_duplicates;
+
+	params->key_size = hashtest_key_lens[cycle];
+	params->cycle = cycle;
+
+	/* Reset all arrays */
+	for (i = 0; i < params->key_size; i++)
+		keys[0][i] = 0;
+
+	/* Generate a list of keys, some of which may be duplicates */
+	for (i = 0; i < KEYS_TO_ADD; i++) {
+		for (j = 0; j < params->key_size; j++)
+			keys[i][j] = rte_rand() & 0xFF;
+
+		data[i] = rte_rand() & ((1 << RTE_EFD_VALUE_NUM_BITS) - 1);
+	}
+
+	/* Remove duplicates from the keys array */
+	do {
+		num_duplicates = 0;
+
+		/* Sort the list of keys to make it easier to find duplicates */
+		qsort(keys, KEYS_TO_ADD, MAX_KEYSIZE, key_compare);
+
+		/* Sift through the list of keys and look for duplicates */
+		int num_duplicates = 0;
+		for (i = 0; i < KEYS_TO_ADD - 1; i++) {
+			if (memcmp(keys[i], keys[i + 1], params->key_size) == 0) {
+				/* This key already exists, try again */
+				num_duplicates++;
+				for (j = 0; j < params->key_size; j++)
+					keys[i][j] = rte_rand() & 0xFF;
+			}
+		}
+	} while (num_duplicates != 0);
+
+	/* Shuffle the random values again */
+	shuffle_input_keys(params);
+
+	params->efd_table = rte_efd_create("test_efd_perf",
+			MAX_ENTRIES, params->key_size,
+			efd_get_all_sockets_bitmask(), test_socket_id);
+	TEST_ASSERT_NOT_NULL(params->efd_table, "Error creating the efd table\n");
+
+	return 0;
+}
+
+static int
+timed_adds(struct efd_perf_params *params)
+{
+	const uint64_t start_tsc = rte_rdtsc();
+	unsigned int i, a;
+	int32_t ret;
+
+	for (i = 0; i < KEYS_TO_ADD; i++) {
+		ret = rte_efd_update(params->efd_table, test_socket_id, keys[i],
+				data[i]);
+		if (ret != 0) {
+			printf("Error %d in rte_efd_update - key=0x", ret);
+			for (a = 0; a < params->key_size; a++)
+				printf("%02x", keys[i][a]);
+			printf(" value=%d\n", data[i]);
+
+			return -1;
+		}
+	}
+
+	const uint64_t end_tsc = rte_rdtsc();
+	const uint64_t time_taken = end_tsc - start_tsc;
+
+	cycles[params->cycle][ADD] = time_taken / KEYS_TO_ADD;
+	return 0;
+}
+
+static int
+timed_lookups(struct efd_perf_params *params)
+{
+	unsigned int i, j, a;
+	const uint64_t start_tsc = rte_rdtsc();
+	efd_value_t ret_data;
+
+	for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) {
+		for (j = 0; j < KEYS_TO_ADD; j++) {
+			ret_data = rte_efd_lookup(params->efd_table,
+					test_socket_id, keys[j]);
+			if (ret_data != data[j]) {
+				printf("Value mismatch using rte_efd_lookup: "
+						"key #%d (0x", i);
+				for (a = 0; a < params->key_size; a++)
+					printf("%02x", keys[i][a]);
+				printf(")\n");
+				printf("  Expected %d, got %d\n", data[i],
+						ret_data);
+
+				return -1;
+			}
+
+		}
+	}
+
+	const uint64_t end_tsc = rte_rdtsc();
+	const uint64_t time_taken = end_tsc - start_tsc;
+
+	cycles[params->cycle][LOOKUP] = time_taken / NUM_LOOKUPS;
+
+	return 0;
+}
+
+static int
+timed_lookups_multi(struct efd_perf_params *params)
+{
+	unsigned int i, j, k, a;
+	efd_value_t result[RTE_EFD_BURST_MAX] = {0};
+	const void *keys_burst[RTE_EFD_BURST_MAX];
+	const uint64_t start_tsc = rte_rdtsc();
+
+	for (i = 0; i < NUM_LOOKUPS / KEYS_TO_ADD; i++) {
+		for (j = 0; j < KEYS_TO_ADD / RTE_EFD_BURST_MAX; j++) {
+			for (k = 0; k < RTE_EFD_BURST_MAX; k++)
+				keys_burst[k] = keys[j * RTE_EFD_BURST_MAX + k];
+
+			rte_efd_lookup_bulk(params->efd_table, test_socket_id,
+					RTE_EFD_BURST_MAX,
+					keys_burst, result);
+
+			for (k = 0; k < RTE_EFD_BURST_MAX; k++) {
+				uint32_t data_idx = j * RTE_EFD_BURST_MAX + k;
+				if (result[k] != data[data_idx]) {
+					printf("Value mismatch using "
+						"rte_efd_lookup_bulk: key #%d "
+						"(0x", i);
+					for (a = 0; a < params->key_size; a++)
+						printf("%02x",
+							keys[data_idx][a]);
+					printf(")\n");
+					printf("  Expected %d, got %d\n",
+						data[data_idx], result[k]);
+
+					return -1;
+				}
+			}
+		}
+	}
+
+	const uint64_t end_tsc = rte_rdtsc();
+	const uint64_t time_taken = end_tsc - start_tsc;
+
+	cycles[params->cycle][LOOKUP_MULTI] = time_taken / NUM_LOOKUPS;
+
+	return 0;
+}
+
+static int
+timed_deletes(struct efd_perf_params *params)
+{
+	unsigned int i, a;
+	const uint64_t start_tsc = rte_rdtsc();
+	int32_t ret;
+
+	for (i = 0; i < KEYS_TO_ADD; i++) {
+		ret = rte_efd_delete(params->efd_table, test_socket_id, keys[i],
+				NULL);
+
+		if (ret != 0) {
+			printf("Error %d in rte_efd_delete - key=0x", ret);
+			for (a = 0; a < params->key_size; a++)
+				printf("%02x", keys[i][a]);
+			printf("\n");
+
+			return -1;
+		}
+	}
+
+	const uint64_t end_tsc = rte_rdtsc();
+	const uint64_t time_taken = end_tsc - start_tsc;
+
+	cycles[params->cycle][DELETE] = time_taken / KEYS_TO_ADD;
+
+	return 0;
+}
+
+static void
+perform_frees(struct efd_perf_params *params)
+{
+	if (params->efd_table != NULL) {
+		rte_efd_free(params->efd_table);
+		params->efd_table = NULL;
+	}
+}
+
+static int
+exit_with_fail(const char *testname, struct efd_perf_params *params,
+		unsigned int i)
+{
+
+	printf("<<<<<Test %s failed at keysize %d iteration %d >>>>>\n",
+			testname, hashtest_key_lens[params->cycle], i);
+	perform_frees(params);
+	return -1;
+}
+
+static int
+run_all_tbl_perf_tests(void)
+{
+	unsigned int i, j;
+	struct efd_perf_params params;
+
+	printf("Measuring performance, please wait\n");
+	fflush(stdout);
+
+	test_socket_id = rte_socket_id();
+
+	for (i = 0; i < NUM_KEYSIZES; i++) {
+
+		if (setup_keys_and_data(&params, i) < 0) {
+			printf("Could not create keys/data/table\n");
+			return -1;
+		}
+
+		if (timed_adds(&params) < 0)
+			return exit_with_fail("timed_adds", &params, i);
+
+		for (j = 0; j < NUM_SHUFFLES; j++)
+			shuffle_input_keys(&params);
+
+		if (timed_lookups(&params) < 0)
+			return exit_with_fail("timed_lookups", &params, i);
+
+		if (timed_lookups_multi(&params) < 0)
+			return exit_with_fail("timed_lookups_multi", &params, i);
+
+		if (timed_deletes(&params) < 0)
+			return exit_with_fail("timed_deletes", &params, i);
+
+		/* Print a dot to show progress on operations */
+		printf(".");
+		fflush(stdout);
+
+		perform_frees(&params);
+	}
+
+	printf("\nResults (in CPU cycles/operation)\n");
+	printf("-----------------------------------\n");
+	printf("\n%-18s%-18s%-18s%-18s%-18s\n",
+			"Keysize", "Add", "Lookup", "Lookup_bulk", "Delete");
+	for (i = 0; i < NUM_KEYSIZES; i++) {
+		printf("%-18d", hashtest_key_lens[i]);
+		for (j = 0; j < NUM_OPERATIONS; j++)
+			printf("%-18"PRIu64, cycles[i][j]);
+		printf("\n");
+	}
+	return 0;
+}
+
+static int
+test_efd_perf(void)
+{
+
+	if (run_all_tbl_perf_tests() < 0)
+		return -1;
+
+	return 0;
+}
+
+REGISTER_TEST_COMMAND(efd_perf_autotest, test_efd_perf);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 1/5] efd: new Elastic Flow Distributor library
From: Pablo de Lara @ 2017-01-15 12:04 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara, Byron Marohn, Saikrishna Edupuganti
In-Reply-To: <1484481875-126335-1-git-send-email-pablo.de.lara.guarch@intel.com>

Elastic Flow Distributor (EFD) is a distributor library that uses
perfect hashing to determine a target/value for a given incoming flow key.
It has the following advantages:

- First, because it uses perfect hashing, it does not store
  the key itself and hence lookup performance is not dependent
  on the key size.

- Second, the target/value can be any arbitrary value hence
  the system designer and/or operator can better optimize service rates
  and inter-cluster network traffic locating.

- Third, since the storage requirement is much smaller than a hash-based
  flow table (i.e. better fit for CPU cache), EFD can scale to
  millions of flow keys.
  Finally, with current optimized library implementation performance
  is fully scalable with number of CPU cores.

Signed-off-by: Byron Marohn <byron.marohn@intel.com>
Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
Signed-off-by: Saikrishna Edupuganti <saikrishna.edupuganti@intel.com>
Acked-by: Christian Maciocco <christian.maciocco@intel.com>
---
 MAINTAINERS                             |    5 +
 config/common_base                      |    5 +
 doc/api/doxy-api-index.md               |    3 +-
 doc/api/doxy-api.conf                   |    1 +
 doc/guides/rel_notes/release_17_02.rst  |   12 +
 lib/Makefile                            |    3 +-
 lib/librte_eal/common/include/rte_log.h |    3 +-
 lib/librte_efd/Makefile                 |   56 ++
 lib/librte_efd/rte_efd.c                | 1354 +++++++++++++++++++++++++++++++
 lib/librte_efd/rte_efd.h                |  294 +++++++
 lib/librte_efd/rte_efd_version.map      |   12 +
 mk/rte.app.mk                           |    3 +-
 12 files changed, 1747 insertions(+), 4 deletions(-)
 create mode 100644 lib/librte_efd/Makefile
 create mode 100644 lib/librte_efd/rte_efd.c
 create mode 100644 lib/librte_efd/rte_efd.h
 create mode 100644 lib/librte_efd/rte_efd_version.map

diff --git a/MAINTAINERS b/MAINTAINERS
index 9645c9b..9c60d67 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -528,6 +528,11 @@ F: app/test/test_acl.*
 F: examples/l3fwd-acl/
 F: doc/guides/sample_app_ug/l3_forward_access_ctrl.rst
 
+EFD
+M: Byron Marohn <byron.marohn@intel.com>
+M: Pablo de Lara Guarch <pablo.de.lara.guarch@intel.com>
+F: lib/librte_efd/
+
 Hashes
 M: Bruce Richardson <bruce.richardson@intel.com>
 M: Pablo de Lara <pablo.de.lara.guarch@intel.com>
diff --git a/config/common_base b/config/common_base
index 8e9dcfa..869d8fb 100644
--- a/config/common_base
+++ b/config/common_base
@@ -467,6 +467,11 @@ CONFIG_RTE_LIBRTE_HASH=y
 CONFIG_RTE_LIBRTE_HASH_DEBUG=n
 
 #
+# Compile librte_efd
+#
+CONFIG_RTE_LIBRTE_EFD=y
+
+#
 # Compile librte_jobstats
 #
 CONFIG_RTE_LIBRTE_JOBSTATS=y
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 72d59b2..0d34e2f 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -90,7 +90,8 @@ There are many libraries, so their headers may be grouped by topics:
   [frag/reass]         (@ref rte_ip_frag.h),
   [LPM IPv4 route]     (@ref rte_lpm.h),
   [LPM IPv6 route]     (@ref rte_lpm6.h),
-  [ACL]                (@ref rte_acl.h)
+  [ACL]                (@ref rte_acl.h),
+  [EFD]                (@ref rte_efd.h)
 
 - **QoS**:
   [metering]           (@ref rte_meter.h),
diff --git a/doc/api/doxy-api.conf b/doc/api/doxy-api.conf
index b340fcf..6892315 100644
--- a/doc/api/doxy-api.conf
+++ b/doc/api/doxy-api.conf
@@ -40,6 +40,7 @@ INPUT                   = doc/api/doxy-api-index.md \
                           lib/librte_compat \
                           lib/librte_cryptodev \
                           lib/librte_distributor \
+                          lib/librte_efd \
                           lib/librte_ether \
                           lib/librte_hash \
                           lib/librte_ip_frag \
diff --git a/doc/guides/rel_notes/release_17_02.rst b/doc/guides/rel_notes/release_17_02.rst
index 180af82..5023038 100644
--- a/doc/guides/rel_notes/release_17_02.rst
+++ b/doc/guides/rel_notes/release_17_02.rst
@@ -53,6 +53,18 @@ New Features
   information.
 
 
+* **Added Elastic Flow Distributor library (rte_efd).**
+
+  This new library uses perfect hashing to determine a target/value for a
+  given incoming flow key.
+
+  It does not store the key itself for lookup operations, and therefore,
+  lookup performance is not dependent on the key size. Also, the target/value
+  can be any arbitrary value (8 bits by default). Finally, the storage requirement
+  is much smaller than a hash-based flow table and therefore, it can better fit for
+  CPU cache, being able to scale to millions of flow keys.
+
+
 Resolved Issues
 ---------------
 
diff --git a/lib/Makefile b/lib/Makefile
index 990f23a..4178325 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,6 +1,6 @@
 #   BSD LICENSE
 #
-#   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+#   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
 #   All rights reserved.
 #
 #   Redistribution and use in source and binary forms, with or without
@@ -43,6 +43,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether
 DIRS-$(CONFIG_RTE_LIBRTE_CRYPTODEV) += librte_cryptodev
 DIRS-$(CONFIG_RTE_LIBRTE_VHOST) += librte_vhost
 DIRS-$(CONFIG_RTE_LIBRTE_HASH) += librte_hash
+DIRS-$(CONFIG_RTE_LIBRTE_EFD) += librte_efd
 DIRS-$(CONFIG_RTE_LIBRTE_LPM) += librte_lpm
 DIRS-$(CONFIG_RTE_LIBRTE_ACL) += librte_acl
 DIRS-$(CONFIG_RTE_LIBRTE_NET) += librte_net
diff --git a/lib/librte_eal/common/include/rte_log.h b/lib/librte_eal/common/include/rte_log.h
index 671e274..954b96c 100644
--- a/lib/librte_eal/common/include/rte_log.h
+++ b/lib/librte_eal/common/include/rte_log.h
@@ -1,7 +1,7 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
  *   All rights reserved.
  *
  *   Redistribution and use in source and binary forms, with or without
@@ -79,6 +79,7 @@ extern struct rte_logs rte_logs;
 #define RTE_LOGTYPE_PIPELINE 0x00008000 /**< Log related to pipeline. */
 #define RTE_LOGTYPE_MBUF    0x00010000 /**< Log related to mbuf. */
 #define RTE_LOGTYPE_CRYPTODEV 0x00020000 /**< Log related to cryptodev. */
+#define RTE_LOGTYPE_EFD     0x00040000 /**< Log related to EFD. */
 
 /* these log types can be used in an application */
 #define RTE_LOGTYPE_USER1   0x01000000 /**< User-defined log type 1. */
diff --git a/lib/librte_efd/Makefile b/lib/librte_efd/Makefile
new file mode 100644
index 0000000..58d34af
--- /dev/null
+++ b/lib/librte_efd/Makefile
@@ -0,0 +1,56 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_efd.a
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR)
+
+EXPORT_MAP := rte_efd_version.map
+
+LIBABIVER := 1
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_EFD) := rte_efd.c
+
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_EFD)-include := rte_efd.h
+
+# this lib depends upon:
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_mbuf
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_mempool
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_eal
+DEPDIRS-$(CONFIG_RTE_LIBRTE_EFD) += lib/librte_ether
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_efd/rte_efd.c b/lib/librte_efd/rte_efd.c
new file mode 100644
index 0000000..917e076
--- /dev/null
+++ b/lib/librte_efd/rte_efd.c
@@ -0,0 +1,1354 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <immintrin.h>
+#include <math.h>
+#include <sys/queue.h>
+
+#include <rte_log.h>
+#include <rte_eal_memconfig.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <rte_prefetch.h>
+#include <rte_branch_prediction.h>
+#include <rte_memcpy.h>
+#include <rte_ring.h>
+#include <rte_jhash.h>
+#include <rte_hash_crc.h>
+
+#include "rte_efd.h"
+
+#define EFD_KEY(key_idx, table) (table->keys + ((key_idx) * table->key_len))
+/** Hash function used to determine chunk_id and bin_id for a group */
+#define EFD_HASH(key, table) \
+	(uint32_t)(rte_jhash(key, table->key_len, 0xbc9f1d34))
+/** Hash function used as constant component of perfect hash search */
+#define EFD_HASHFUNCA(key, table) \
+	(uint32_t)(rte_hash_crc(key, table->key_len, 0xbc9f1d35))
+/** Hash function used as multiplicative component of perfect hash search */
+#define EFD_HASHFUNCB(key, table) \
+	(uint32_t)(rte_hash_crc(key, table->key_len, 0xbc9f1d36))
+
+/*************************************************************************
+ * Fixed constants
+ *************************************************************************/
+
+/* These parameters are fixed by the efd_bin_to_group balancing table */
+#define EFD_CHUNK_NUM_GROUPS (64)
+#define EFD_CHUNK_NUM_BINS   (256)
+#define EFD_CHUNK_NUM_BIN_TO_GROUP_SETS \
+	(EFD_CHUNK_NUM_BINS / EFD_CHUNK_NUM_GROUPS)
+
+/*
+ * Target number of rules that each chunk is created to handle.
+ * Used when initially allocating the table
+ */
+#define EFD_TARGET_CHUNK_NUM_RULES  \
+	(EFD_CHUNK_NUM_GROUPS * EFD_TARGET_GROUP_NUM_RULES)
+/*
+ * Max number of rules that each chunk is created to handle.
+ * Used when initially allocating the table
+ */
+#define EFD_TARGET_CHUNK_MAX_NUM_RULES  \
+	(EFD_CHUNK_NUM_GROUPS * EFD_MAX_GROUP_NUM_RULES)
+
+/** This is fixed based on the bin_to_group permutation array */
+#define EFD_MAX_GROUP_NUM_BINS (16)
+
+/**
+ * The end of the chunks array needs some extra padding to ensure
+ * that vectorization over-reads on the last online chunk stay within
+allocated memory
+ */
+#define EFD_NUM_CHUNK_PADDING_BYTES (256)
+
+#define EFD_LOOKUPTBL_SHIFT (32 - 4)
+typedef uint16_t efd_lookuptbl_t;
+typedef uint16_t efd_hashfunc_t;
+
+/* All different signature compare functions */
+enum rte_efd_compare_function {
+	RTE_HASH_COMPARE_SCALAR = 0,
+	RTE_HASH_COMPARE_AVX2,
+	RTE_HASH_COMPARE_NUM
+};
+
+TAILQ_HEAD(rte_efd_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_efd_tailq = {
+	.name = "RTE_EFD",
+};
+EAL_REGISTER_TAILQ(rte_efd_tailq);
+
+/** Internal permutation array used to shuffle bins into pseudorandom groups */
+const uint32_t efd_bin_to_group[EFD_CHUNK_NUM_BIN_TO_GROUP_SETS][EFD_CHUNK_NUM_BINS] = {
+	{
+		0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
+		4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
+		8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
+		12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
+		16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
+		20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
+		24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
+		28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
+		32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
+		36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
+		40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
+		44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47,
+		48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51,
+		52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55,
+		56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59,
+		60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63
+	},
+	{
+		34, 33, 48, 59, 0, 21, 36, 18, 9, 49, 54, 38, 51, 23, 31, 5,
+		44, 23, 37, 52, 11, 4, 58, 20, 38, 40, 38, 22, 26, 28, 42, 6,
+		46, 16, 31, 28, 46, 14, 60, 0, 35, 53, 16, 58, 16, 29, 39, 7,
+		1, 54, 15, 11, 48, 3, 62, 9, 58, 5, 30, 43, 17, 7, 36, 34,
+		6, 36, 2, 14, 10, 1, 47, 47, 20, 45, 62, 56, 34, 25, 39, 18,
+		51, 41, 61, 25, 56, 40, 41, 37, 52, 35, 30, 57, 11, 42, 37, 27,
+		54, 19, 26, 13, 48, 31, 46, 15, 12, 10, 16, 20, 43, 17, 12, 55,
+		45, 18, 8, 41, 7, 31, 42, 63, 12, 14, 21, 57, 24, 40, 5, 41,
+		13, 44, 23, 59, 25, 57, 52, 50, 62, 1, 2, 49, 32, 57, 26, 43,
+		56, 60, 55, 5, 49, 6, 3, 50, 46, 39, 27, 33, 17, 4, 53, 13,
+		2, 19, 36, 51, 63, 0, 22, 33, 59, 28, 29, 23, 45, 33, 53, 27,
+		22, 21, 40, 56, 4, 18, 44, 47, 28, 17, 4, 50, 21, 62, 8, 39,
+		0, 8, 15, 24, 29, 24, 9, 11, 48, 61, 35, 55, 43, 1, 54, 42,
+		53, 60, 22, 3, 32, 52, 25, 8, 15, 60, 7, 55, 27, 63, 19, 10,
+		63, 24, 61, 19, 12, 38, 6, 29, 13, 37, 10, 3, 45, 32, 32, 30,
+		49, 61, 44, 14, 20, 58, 35, 30, 2, 26, 34, 51, 9, 59, 47, 50
+	},
+	{
+		32, 35, 32, 34, 55, 5, 6, 23, 49, 11, 6, 23, 52, 37, 29, 54,
+		55, 40, 63, 50, 29, 52, 61, 25, 12, 56, 39, 38, 29, 11, 46, 1,
+		40, 11, 19, 56, 7, 28, 51, 16, 15, 48, 21, 51, 60, 31, 14, 22,
+		41, 47, 59, 56, 53, 28, 58, 26, 43, 27, 41, 33, 24, 52, 44, 38,
+		13, 59, 48, 51, 60, 15, 3, 30, 15, 0, 10, 62, 44, 14, 28, 51,
+		38, 2, 41, 26, 25, 49, 10, 12, 55, 57, 27, 35, 19, 33, 0, 30,
+		5, 36, 47, 53, 5, 53, 20, 43, 34, 37, 52, 41, 21, 63, 59, 9,
+		24, 1, 45, 24, 39, 44, 45, 16, 9, 17, 7, 50, 57, 22, 18, 28,
+		25, 45, 2, 40, 58, 15, 17, 3, 1, 27, 61, 39, 19, 0, 19, 21,
+		57, 62, 54, 60, 54, 40, 48, 33, 36, 37, 4, 42, 1, 43, 58, 8,
+		13, 42, 10, 56, 35, 22, 48, 61, 63, 10, 49, 9, 24, 9, 25, 57,
+		33, 18, 13, 31, 42, 36, 36, 55, 30, 37, 53, 34, 59, 4, 4, 23,
+		8, 16, 58, 14, 30, 11, 12, 63, 49, 62, 2, 39, 47, 22, 2, 60,
+		18, 8, 46, 31, 6, 20, 32, 29, 46, 42, 20, 31, 32, 61, 34, 4,
+		47, 26, 20, 43, 26, 21, 7, 3, 16, 35, 18, 44, 27, 62, 13, 23,
+		6, 50, 12, 8, 45, 17, 3, 46, 50, 7, 14, 5, 17, 54, 38, 0
+	},
+	{
+		29, 56, 5, 7, 54, 48, 23, 37, 35, 44, 52, 40, 33, 49, 60, 0,
+		59, 51, 28, 12, 41, 26, 2, 23, 34, 5, 59, 40, 3, 19, 6, 26,
+		35, 53, 45, 49, 29, 57, 28, 62, 58, 59, 19, 53, 59, 62, 6, 54,
+		13, 15, 48, 50, 45, 21, 41, 12, 34, 40, 24, 56, 19, 21, 35, 18,
+		55, 45, 9, 61, 47, 61, 19, 15, 16, 39, 17, 31, 3, 51, 21, 50,
+		17, 25, 25, 11, 44, 16, 18, 28, 14, 2, 37, 61, 58, 27, 62, 4,
+		14, 17, 1, 9, 46, 28, 37, 0, 53, 43, 57, 7, 57, 46, 21, 41,
+		39, 14, 52, 60, 44, 53, 49, 60, 49, 63, 13, 11, 29, 1, 55, 47,
+		55, 12, 60, 43, 54, 37, 13, 6, 42, 10, 36, 13, 9, 8, 34, 51,
+		31, 32, 12, 7, 57, 2, 26, 14, 3, 30, 63, 3, 32, 1, 5, 11,
+		27, 24, 26, 44, 31, 23, 56, 38, 62, 0, 40, 30, 6, 23, 38, 2,
+		47, 5, 15, 27, 16, 10, 31, 25, 22, 63, 30, 25, 20, 33, 32, 50,
+		29, 43, 55, 10, 50, 45, 56, 20, 4, 7, 27, 46, 11, 16, 22, 52,
+		35, 20, 41, 54, 46, 33, 42, 18, 63, 8, 22, 58, 36, 4, 51, 42,
+		38, 32, 38, 22, 17, 0, 47, 8, 48, 8, 48, 1, 61, 36, 33, 20,
+		24, 39, 39, 18, 30, 36, 9, 43, 42, 24, 10, 58, 4, 15, 34, 52
+	},
+};
+
+/*************************************************************************
+ * Offline region structures
+ *************************************************************************/
+
+/** Online group containing number of rules, values, keys and their bins
+ * for EFD_MAX_GROUP_NUM_RULES rules.
+ */
+struct efd_offline_group_rules {
+	uint32_t num_rules;
+	/**< Sum of the number of rules in all bins assigned to this group. */
+
+	uint32_t key_idx[EFD_MAX_GROUP_NUM_RULES];
+	/**< Array with all keys of the group. */
+	efd_value_t value[EFD_MAX_GROUP_NUM_RULES];
+	/**< Array with all values of the keys of the group. */
+
+	uint8_t bin_id[EFD_MAX_GROUP_NUM_RULES];
+	/**< Stores the bin for each correspending key to
+	 * avoid having to recompute it
+	 */
+};
+
+/** Offline chunk record, containing EFD_TARGET_CHUNK_NUM_RULES rules.
+ * Those rules are split into EFD_CHUNK_NUM_GROUPS groups per chunk.
+ */
+struct efd_offline_chunk_rules {
+	uint16_t num_rules;
+	/**< Number of rules in the entire chunk;
+	 * used to detect unbalanced groups
+	 */
+
+	struct efd_offline_group_rules group_rules[EFD_CHUNK_NUM_GROUPS];
+	/**< Array of all groups in the chunk. */
+};
+
+/*************************************************************************
+ * Online region structures
+ *************************************************************************/
+
+/** Online group containing values for EFD_MAX_GROUP_NUM_RULES rules. */
+struct efd_online_group_entry {
+	efd_hashfunc_t hash_idx[RTE_EFD_VALUE_NUM_BITS];
+	efd_lookuptbl_t lookup_table[RTE_EFD_VALUE_NUM_BITS];
+} __attribute__((__packed__));
+
+/**
+ * A single chunk record, containing EFD_TARGET_CHUNK_NUM_RULES rules.
+ * Those rules are split into EFD_CHUNK_NUM_GROUPS groups per chunk.
+ */
+struct efd_online_chunk {
+	uint8_t bin_choice_list[(EFD_CHUNK_NUM_BINS * 2 + 7) / 8];
+	/**< This is a packed indirection index into the 'groups' array.
+	 * Each byte contains four two-bit values which index into
+	 * the efd_bin_to_group array.
+	 * The efd_bin_to_group array returns the index into the groups array
+	 */
+
+	struct efd_online_group_entry groups[EFD_CHUNK_NUM_GROUPS];
+	/**< Array of all the groups in the chunk. */
+} __attribute__((__packed__));
+
+/**
+ * EFD table structure
+ */
+struct rte_efd_table {
+	char name[RTE_EFD_NAMESIZE]; /**< Name of the efd table. */
+
+	uint32_t key_len; /**< Length of the key stored offline */
+
+	uint32_t max_num_rules;
+	/**< Static maximum number of entries the table was constructed to hold. */
+
+	uint32_t num_rules;
+	/**< Number of entries currently in the table . */
+
+	uint32_t num_chunks;
+	/**< Number of chunks in the table needed to support num_rules. */
+
+	uint32_t num_chunks_shift;
+	/**< Bits to shift to get chunk id, instead of dividing by num_chunk. */
+
+	enum rte_efd_compare_function cmp_fn;
+	/**< Indicates which compare function to use. */
+
+	struct efd_online_chunk *chunks[RTE_MAX_NUMA_NODES];
+	/**< Dynamic array of size num_chunks of chunk records. */
+
+	struct efd_offline_chunk_rules *offline_chunks;
+	/**< Dynamic array of size num_chunks of key-value pairs. */
+
+	struct rte_ring *free_slots;
+	/**< Ring that stores all indexes of the free slots in the key table */
+
+	uint8_t *keys; /**< Dynamic array of size max_num_rules of keys */
+};
+
+/**
+ * Computes the chunk ID for a given key hash
+ *
+ * @param table
+ *   EFD table to reference
+ * @param hashed_key
+ *   32-bit key hash returned by EFD_HASH
+ *
+ * @return
+ *   chunk ID containing this key hash
+ */
+static inline uint32_t
+efd_get_chunk_id(const struct rte_efd_table * const table,
+		const uint32_t hashed_key)
+{
+	return hashed_key & (table->num_chunks - 1);
+}
+
+/**
+ * Computes the bin ID for a given key hash
+ *
+ * @param table
+ *   EFD table to reference
+ * @param hashed_key
+ *   32-bit key hash returned by EFD_HASH
+ *
+ * @return bin ID containing this key hash
+ */
+static inline uint32_t
+efd_get_bin_id(const struct rte_efd_table * const table,
+		const uint32_t hashed_key)
+{
+	return (hashed_key >> table->num_chunks_shift) & (EFD_CHUNK_NUM_BINS - 1);
+}
+
+/**
+ * Looks up the current permutation choice for a particular bin in the online table
+ *
+ * @param table
+ *  EFD table to reference
+ * @param socket_id
+ *   Socket ID to use to look up existing values (ideally caller's socket id)
+ * @param chunk_id
+ *   Chunk ID of bin to look up
+ * @param bin_id
+ *   Bin ID to look up
+ *
+ * @return
+ *   Currently active permutation choice in the online table
+ */
+static inline uint8_t
+efd_get_choice(const struct rte_efd_table * const table,
+		const unsigned int socket_id, const uint32_t chunk_id,
+		const uint32_t bin_id)
+{
+	struct efd_online_chunk *chunk = &table->chunks[socket_id][chunk_id];
+
+	/*
+	 * Grab the chunk (byte) that contains the choices
+	 * for four neighboring bins.
+	 */
+	uint8_t choice_chunk =
+			chunk->bin_choice_list[bin_id / EFD_CHUNK_NUM_BIN_TO_GROUP_SETS];
+
+	/*
+	 * Compute the offset into the chunk that contains
+	 * the group_id lookup position
+	 */
+	int offset = (bin_id & 0x3) * 2;
+
+	/* Extract from the byte just the desired lookup position */
+	return (uint8_t) ((choice_chunk >> offset) & 0x3);
+}
+
+/**
+ * Compute the chunk_id and bin_id for a given key
+ *
+ * @param table
+ *   EFD table to reference
+ * @param key
+ *   Key to hash and find location of
+ * @param chunk_id
+ *   Computed chunk ID
+ * @param bin_id
+ *   Computed bin ID
+ *
+ */
+static inline void
+efd_compute_ids(const struct rte_efd_table * const table,
+		const void *key, uint32_t * const chunk_id, uint32_t * const bin_id)
+{
+	/* Compute the position of the entry in the hash table */
+	uint32_t h = EFD_HASH(key, table);
+
+	/* Compute the chunk_id where that entry can be found */
+	*chunk_id = efd_get_chunk_id(table, h);
+
+	/*
+	 * Compute the bin within that chunk where the entry
+	 * can be found (0 - 255)
+	 */
+	*bin_id = efd_get_bin_id(table, h);
+}
+
+/**
+ * Search for a hash function for a group that satisfies all group results
+ */
+static inline int
+efd_search_hash(struct rte_efd_table * const table,
+		const struct efd_offline_group_rules * const off_group,
+		struct efd_online_group_entry * const on_group)
+{
+	efd_hashfunc_t hash_idx;
+	efd_hashfunc_t start_hash_idx[RTE_EFD_VALUE_NUM_BITS];
+	efd_lookuptbl_t start_lookup_table[RTE_EFD_VALUE_NUM_BITS];
+
+	uint32_t i, j, rule_id;
+	uint32_t hash_val_a[EFD_MAX_GROUP_NUM_RULES];
+	uint32_t hash_val_b[EFD_MAX_GROUP_NUM_RULES];
+	uint32_t hash_val[EFD_MAX_GROUP_NUM_RULES];
+
+
+	rte_prefetch0(off_group->value);
+
+	/*
+	 * Prepopulate the hash_val tables by running the two hash functions
+	 * for each provided rule
+	 */
+	for (i = 0; i < off_group->num_rules; i++) {
+		void *key_stored = EFD_KEY(off_group->key_idx[i], table);
+		hash_val_b[i] = EFD_HASHFUNCB(key_stored, table);
+		hash_val_a[i] = EFD_HASHFUNCA(key_stored, table);
+	}
+
+	for (i = 0; i < RTE_EFD_VALUE_NUM_BITS; i++) {
+		hash_idx = on_group->hash_idx[i];
+		start_hash_idx[i] = hash_idx;
+		start_lookup_table[i] = on_group->lookup_table[i];
+
+		do {
+			efd_lookuptbl_t lookup_table = 0;
+			efd_lookuptbl_t lookup_table_complement = 0;
+
+			for (rule_id = 0; rule_id < off_group->num_rules; rule_id++)
+				hash_val[rule_id] = hash_val_a[rule_id] + (hash_idx *
+					hash_val_b[rule_id]);
+
+			/*
+			 * The goal here is to find a hash function for this
+			 * particular bit entry that meets the following criteria:
+			 * The most significant bits of the hash result define a
+			 * shift into the lookup table where the bit will be stored
+			 */
+
+			/* Iterate over each provided rule */
+			for (rule_id = 0; rule_id < off_group->num_rules;
+					rule_id++) {
+				/*
+				 * Use the few most significant bits (number based on
+				 * EFD_LOOKUPTBL_SIZE) to see what position the
+				 * expected bit should be set in the lookup_table
+				 */
+				uint32_t bucket_idx = hash_val[rule_id] >>
+						EFD_LOOKUPTBL_SHIFT;
+
+				/*
+				 * Get the current bit of interest.
+				 * This only find an appropriate hash function
+				 * for one bit at a time of the rule
+				 */
+				efd_lookuptbl_t expected =
+						(off_group->value[rule_id] >> i) & 0x1;
+
+				/*
+				 * Add the expected bit (if set) to a map
+				 * (lookup_table). Also set its complement
+				 * in lookup_table_complement
+				 */
+				lookup_table |= expected << bucket_idx;
+				lookup_table_complement |= (1 - expected)
+						<< bucket_idx;
+
+				/*
+				 * If ever the hash function of two different
+				 * elements result in different values at the
+				 * same location in the lookup_table,
+				 * the current hash_idx is not valid.
+				 */
+				if (lookup_table & lookup_table_complement)
+					break;
+			}
+
+			/*
+			 * Check if the previous loop completed without
+			 * breaking early
+			 */
+			if (rule_id == off_group->num_rules) {
+				/*
+				 * Current hash function worked, store it
+				 * for the current group
+				 */
+				on_group->hash_idx[i] = hash_idx;
+				on_group->lookup_table[i] = lookup_table;
+
+				/*
+				 * Make sure that the hash function has changed
+				 * from the starting value
+				 */
+				hash_idx = start_hash_idx[i] + 1;
+				break;
+			}
+			hash_idx++;
+
+		} while (hash_idx != start_hash_idx[i]);
+
+		/* Failed to find perfect hash for this group */
+		if (hash_idx == start_hash_idx[i]) {
+			/*
+			 * Restore previous hash_idx and lookup_table
+			 * for all value bits
+			 */
+			for (j = 0; j < i; j++) {
+				on_group->hash_idx[j] = start_hash_idx[j];
+				on_group->lookup_table[j] = start_lookup_table[j];
+			}
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+struct rte_efd_table *
+rte_efd_create(const char *name, uint32_t max_num_rules, uint32_t key_len,
+		uint8_t online_cpu_socket_bitmask, uint8_t offline_cpu_socket)
+{
+	struct rte_efd_table *table = NULL;
+	uint8_t *key_array = NULL;
+	uint32_t num_chunks, num_chunks_shift;
+	uint8_t socket_id;
+	struct rte_efd_list *efd_list = NULL;
+	struct rte_tailq_entry *te;
+	uint64_t offline_table_size;
+	char ring_name[RTE_RING_NAMESIZE];
+	struct rte_ring *r = NULL;
+	unsigned int i;
+
+	efd_list = RTE_TAILQ_CAST(rte_efd_tailq.head, rte_efd_list);
+
+	if (online_cpu_socket_bitmask == 0) {
+		RTE_LOG(ERR, EFD, "At least one CPU socket must be enabled "
+				"in the bitmask\n");
+		return NULL;
+	}
+
+	if (max_num_rules == 0) {
+		RTE_LOG(ERR, EFD, "Max num rules must be higher than 0\n");
+		return NULL;
+	}
+
+	/*
+	 * Compute the minimum number of chunks (smallest power of 2)
+	 * that can hold all of the rules
+	 */
+	if (max_num_rules % EFD_TARGET_CHUNK_NUM_RULES == 0)
+		num_chunks = rte_align32pow2(max_num_rules /
+			EFD_TARGET_CHUNK_NUM_RULES);
+	else
+		num_chunks = rte_align32pow2((max_num_rules /
+			EFD_TARGET_CHUNK_NUM_RULES) + 1);
+
+	num_chunks_shift = log2(num_chunks);
+
+	rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
+
+	/*
+	 * Guarantee there's no existing: this is normally already checked
+	 * by ring creation above
+	 */
+	TAILQ_FOREACH(te, efd_list, next)
+	{
+		table = (struct rte_efd_table *) te->data;
+		if (strncmp(name, table->name, RTE_EFD_NAMESIZE) == 0)
+			break;
+	}
+
+	table = NULL;
+	if (te != NULL) {
+		rte_errno = EEXIST;
+		te = NULL;
+		goto error_unlock_exit;
+	}
+
+	te = rte_zmalloc("EFD_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, EFD, "tailq entry allocation failed\n");
+		goto error_unlock_exit;
+	}
+
+	/* Create a new EFD table management structure */
+	table = (struct rte_efd_table *) rte_zmalloc_socket(NULL,
+			sizeof(struct rte_efd_table),
+			RTE_CACHE_LINE_SIZE,
+			offline_cpu_socket);
+	if (table == NULL) {
+		RTE_LOG(ERR, EFD, "Allocating EFD table management structure"
+				" on socket %u failed\n",
+				offline_cpu_socket);
+		goto error_unlock_exit;
+	}
+
+
+	RTE_LOG(DEBUG, EFD, "Allocated EFD table management structure "
+			"on socket %u\n", offline_cpu_socket);
+
+	table->max_num_rules = num_chunks * EFD_TARGET_CHUNK_MAX_NUM_RULES;
+	table->num_rules = 0;
+	table->num_chunks = num_chunks;
+	table->num_chunks_shift = num_chunks_shift;
+	table->key_len = key_len;
+
+	/* key_array */
+	key_array = (uint8_t *) rte_zmalloc_socket(NULL,
+			table->max_num_rules * table->key_len,
+			RTE_CACHE_LINE_SIZE,
+			offline_cpu_socket);
+	if (key_array == NULL) {
+		RTE_LOG(ERR, EFD, "Allocating key array"
+				" on socket %u failed\n",
+				offline_cpu_socket);
+		goto error_unlock_exit;
+	}
+	table->keys = key_array;
+	snprintf(table->name, sizeof(table->name), "%s", name);
+
+	RTE_LOG(DEBUG, EFD, "Creating an EFD table with %u chunks,"
+			" which potentially supports %u entries\n",
+			num_chunks, table->max_num_rules);
+
+	/* Make sure all the allocatable table pointers are NULL initially */
+	for (socket_id = 0; socket_id < RTE_MAX_NUMA_NODES; socket_id++)
+		table->chunks[socket_id] = NULL;
+	table->offline_chunks = NULL;
+
+	/*
+	 * Allocate one online table per socket specified
+	 * in the user-supplied bitmask
+	 */
+	uint64_t online_table_size = num_chunks * sizeof(struct efd_online_chunk) +
+			EFD_NUM_CHUNK_PADDING_BYTES;
+
+	for (socket_id = 0; socket_id < RTE_MAX_NUMA_NODES; socket_id++) {
+		if ((online_cpu_socket_bitmask >> socket_id) & 0x01) {
+			/*
+			 * Allocate all of the EFD table chunks (the online portion)
+			 * as a continuous block
+			 */
+			table->chunks[socket_id] =
+				(struct efd_online_chunk *) rte_zmalloc_socket(
+				NULL,
+				online_table_size,
+				RTE_CACHE_LINE_SIZE,
+				socket_id);
+			if (table->chunks[socket_id] == NULL) {
+				RTE_LOG(ERR, EFD,
+						"Allocating EFD online table on "
+						"socket %u failed\n",
+						socket_id);
+				goto error_unlock_exit;
+			}
+			RTE_LOG(DEBUG, EFD,
+					"Allocated EFD online table of size "
+					"%"PRIu64" bytes (%.2f MB) on socket %u\n",
+					online_table_size,
+					(float) online_table_size /
+						(1024.0F * 1024.0F),
+					socket_id);
+		}
+	}
+
+#if defined(RTE_ARCH_X86)
+	if (RTE_EFD_VALUE_NUM_BITS > 3 && rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2))
+		table->cmp_fn = RTE_HASH_COMPARE_AVX2;
+	else
+#endif
+		table->cmp_fn = RTE_HASH_COMPARE_SCALAR;
+
+	/*
+	 * Allocate the EFD table offline portion (with the actual rules
+	 * mapping keys to values) as a continuous block.
+	 * This could be several gigabytes of memory.
+	 */
+	offline_table_size = num_chunks * sizeof(struct efd_offline_chunk_rules);
+	table->offline_chunks =
+			(struct efd_offline_chunk_rules *) rte_zmalloc_socket(NULL,
+			offline_table_size,
+			RTE_CACHE_LINE_SIZE,
+			offline_cpu_socket);
+	if (table->offline_chunks == NULL) {
+		RTE_LOG(ERR, EFD, "Allocating EFD offline table on socket %u "
+				"failed\n", offline_cpu_socket);
+		goto error_unlock_exit;
+	}
+
+	RTE_LOG(DEBUG, EFD,
+			"Allocated EFD offline table of size %"PRIu64" bytes "
+			" (%.2f MB) on socket %u\n", offline_table_size,
+			(float) offline_table_size / (1024.0F * 1024.0F),
+			offline_cpu_socket);
+
+	te->data = (void *) table;
+	TAILQ_INSERT_TAIL(efd_list, te, next);
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	snprintf(ring_name, sizeof(ring_name), "HT_%s", table->name);
+	/* Create ring (Dummy slot index is not enqueued) */
+	r = rte_ring_create(ring_name, rte_align32pow2(table->max_num_rules),
+			offline_cpu_socket, 0);
+	if (r == NULL) {
+		RTE_LOG(ERR, EFD, "memory allocation failed\n");
+		goto error_unlock_exit;
+	}
+
+	/* Populate free slots ring. Entry zero is reserved for key misses. */
+	for (i = 0; i < table->max_num_rules; i++)
+		rte_ring_sp_enqueue(r, (void *) ((uintptr_t) i));
+
+	table->free_slots = r;
+	return table;
+
+error_unlock_exit:
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+	rte_efd_free(table);
+
+	return NULL;
+}
+
+struct rte_efd_table *
+rte_efd_find_existing(const char *name)
+{
+	struct rte_efd_table *table = NULL;
+	struct rte_tailq_entry *te;
+	struct rte_efd_list *efd_list;
+
+	efd_list = RTE_TAILQ_CAST(rte_efd_tailq.head, rte_efd_list);
+
+	rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK);
+
+	TAILQ_FOREACH(te, efd_list, next)
+	{
+		table = (struct rte_efd_table *) te->data;
+		if (strncmp(name, table->name, RTE_EFD_NAMESIZE) == 0)
+			break;
+	}
+	rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	if (te == NULL) {
+		rte_errno = ENOENT;
+		return NULL;
+	}
+	return table;
+}
+
+void
+rte_efd_free(struct rte_efd_table *table)
+{
+	uint8_t socket_id;
+
+	if (table == NULL)
+		return;
+
+	for (socket_id = 0; socket_id < RTE_MAX_NUMA_NODES; socket_id++)
+		rte_free(table->chunks[socket_id]);
+
+	rte_ring_free(table->free_slots);
+	rte_free(table->offline_chunks);
+	rte_free(table->keys);
+	rte_free(table);
+}
+
+/**
+ * Applies a previously computed table entry to the specified table for all
+ * socket-local copies of the online table.
+ * Intended to apply an update for only a single change
+ * to a key/value pair at a time
+ *
+ * @param table
+ *   EFD table to reference
+ * @param socket_id
+ *   Socket ID to use to lookup existing values (ideally caller's socket id)
+ * @param chunk_id
+ *   Chunk index to update
+ * @param group_id
+ *   Group index to update
+ * @param bin_id
+ *   Bin within the group that this update affects
+ * @param new_bin_choice
+ *   Newly chosen permutation which this bin should use - only lower 2 bits
+ * @param new_group_entry
+ *   Previously computed updated chunk/group entry
+ */
+static inline void
+efd_apply_update(struct rte_efd_table * const table, const unsigned int socket_id,
+		const uint32_t chunk_id, const uint32_t group_id,
+		const uint32_t bin_id, const uint8_t new_bin_choice,
+		const struct efd_online_group_entry * const new_group_entry)
+{
+	int i;
+	struct efd_online_chunk *chunk = &table->chunks[socket_id][chunk_id];
+	uint8_t bin_index = bin_id / EFD_CHUNK_NUM_BIN_TO_GROUP_SETS;
+
+	/*
+	 * Grab the current byte that contains the choices
+	 * for four neighboring bins
+	 */
+	uint8_t choice_chunk =
+			chunk->bin_choice_list[bin_index];
+
+
+	/* Compute the offset into the chunk that needs to be updated */
+	int offset = (bin_id & 0x3) * 2;
+
+	/* Zero the two bits of interest and set them to new_bin_choice */
+	choice_chunk = (choice_chunk & (~(0x03 << offset)))
+			| ((new_bin_choice & 0x03) << offset);
+
+	/* Update the online table with the new data across all sockets */
+	for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
+		if (table->chunks[i] != NULL) {
+			memcpy(&(table->chunks[i][chunk_id].groups[group_id]),
+					new_group_entry,
+					sizeof(struct efd_online_group_entry));
+			table->chunks[i][chunk_id].bin_choice_list[bin_index] =
+					choice_chunk;
+		}
+	}
+}
+
+/*
+ * Move the bin from prev group to the new group
+ */
+static inline void
+move_groups(uint32_t bin_id, uint8_t bin_size,
+		struct efd_offline_group_rules *new_group,
+		struct efd_offline_group_rules * const current_group)
+{
+
+	uint8_t empty_idx = 0;
+	unsigned int i;
+
+	if (new_group == current_group)
+		return;
+
+	for (i = 0; i < current_group->num_rules; i++) {
+		/*
+		 * Move keys that belong to the same bin
+		 * to the new group
+		 */
+		if (current_group->bin_id[i] == bin_id) {
+			new_group->key_idx[new_group->num_rules] =
+					current_group->key_idx[i];
+			new_group->value[new_group->num_rules] =
+					current_group->value[i];
+			new_group->bin_id[new_group->num_rules] =
+					current_group->bin_id[i];
+			new_group->num_rules++;
+		} else {
+			if (i != empty_idx) {
+				/*
+				 * Need to move this key towards
+				 * the top of the array
+				 */
+				current_group->key_idx[empty_idx] =
+						current_group->key_idx[i];
+				current_group->value[empty_idx] =
+						current_group->value[i];
+				current_group->bin_id[empty_idx] =
+						current_group->bin_id[i];
+			}
+			empty_idx++;
+		}
+
+	}
+	current_group->num_rules -= bin_size;
+}
+
+/*
+ * Revert group/s to their previous state before
+ * trying to insert/add a new key
+ */
+static inline void
+revert_groups(struct efd_offline_group_rules *previous_group,
+		struct efd_offline_group_rules *current_group, uint8_t bin_size)
+{
+	unsigned int i;
+
+	if (current_group == previous_group)
+		return;
+
+	/* Move keys back to previous group */
+	for (i = current_group->num_rules - bin_size;
+			i < current_group->num_rules; i++) {
+		previous_group->key_idx[previous_group->num_rules] =
+				current_group->key_idx[i];
+		previous_group->value[previous_group->num_rules] =
+				current_group->value[i];
+		previous_group->bin_id[previous_group->num_rules] =
+				current_group->bin_id[i];
+		previous_group->num_rules++;
+	}
+
+	/*
+	 * Decrease number of rules after the move
+	 * in the new group
+	 */
+	current_group->num_rules -= bin_size;
+}
+
+/**
+ * Computes an updated table entry where the supplied key points to a new host.
+ * If no entry exists, one is inserted.
+ *
+ * This function does NOT modify the online table(s)
+ * This function DOES modify the offline table
+ *
+ * @param table
+ *   EFD table to reference
+ * @param socket_id
+ *   Socket ID to use to lookup existing values (ideally caller's socket id)
+ * @param key
+ *   Key to insert
+ * @param value
+ *   Value to associate with key
+ * @param chunk_id
+ *   Chunk ID of the chunk that was modified
+ * @param group_id
+ *   Group ID of the group that was modified
+ * @param bin_id
+ *   Bin ID that was modified
+ * @param new_bin_choice
+ *   Newly chosen permutation which this bin will use
+ * @param entry
+ *   Newly computed online entry to apply later with efd_apply_update
+ *
+ * @return
+ *   RTE_EFD_UPDATE_WARN_GROUP_FULL
+ *     Operation is insert, and the last available space in the
+ *     key's group was just used. Future inserts may fail as groups fill up.
+ *     This operation was still successful, and entry contains a valid update
+ *   RTE_EFD_UPDATE_FAILED
+ *     Either the EFD failed to find a suitable perfect hash or the group was full
+ *     This is a fatal error, and the table is now in an indeterminite state
+ *   RTE_EFD_UPDATE_NO_CHANGE
+ *     Operation resulted in no change to the table (same value already exists)
+ *   0
+ *     Insert or update was successful, and the new efd_online_group_entry
+ *     is stored in *entry
+ *
+ * @warning
+ *   Note that entry will be UNCHANGED if the update has no effect, and thus any
+ *   subsequent use of the entry content will likely be invalid
+ */
+static inline int
+efd_compute_update(struct rte_efd_table * const table,
+		const unsigned int socket_id, const void *key,
+		const efd_value_t value, uint32_t * const chunk_id,
+		uint32_t * const group_id, uint32_t * const bin_id,
+		uint8_t * const new_bin_choice,
+		struct efd_online_group_entry * const entry)
+{
+	unsigned int i;
+	int ret;
+	uint32_t new_idx;
+	void *new_k, *slot_id = NULL;
+	int status = EXIT_SUCCESS;
+	unsigned int found = 0;
+
+	efd_compute_ids(table, key, chunk_id, bin_id);
+
+	struct efd_offline_chunk_rules * const chunk =
+			&table->offline_chunks[*chunk_id];
+	struct efd_offline_group_rules *new_group;
+
+	uint8_t current_choice = efd_get_choice(table, socket_id,
+			*chunk_id, *bin_id);
+	uint32_t current_group_id = efd_bin_to_group[current_choice][*bin_id];
+	struct efd_offline_group_rules * const current_group =
+			&chunk->group_rules[current_group_id];
+	uint8_t bin_size = 0;
+	uint8_t key_changed_index = 0;
+	efd_value_t key_changed_previous_value = 0;
+	uint32_t key_idx_previous = 0;
+
+	/* Scan the current group and see if the key is already present */
+	for (i = 0; i < current_group->num_rules; i++) {
+		if (current_group->bin_id[i] == *bin_id)
+			bin_size++;
+		else
+			continue;
+
+		void *key_stored = EFD_KEY(current_group->key_idx[i], table);
+		if (found == 0 && unlikely(memcmp(key_stored, key,
+				table->key_len) == 0)) {
+			/* Key is already present */
+
+			/*
+			 * If previous value is same as new value,
+			 * no additional work is required
+			 */
+			if (current_group->value[i] == value)
+				return RTE_EFD_UPDATE_NO_CHANGE;
+
+			key_idx_previous = current_group->key_idx[i];
+			key_changed_previous_value = current_group->value[i];
+			key_changed_index = i;
+			current_group->value[i] = value;
+			found = 1;
+		}
+	}
+
+	if (found == 0) {
+		/* Key does not exist. Insert the rule into the bin/group */
+		if (unlikely(current_group->num_rules >= EFD_MAX_GROUP_NUM_RULES)) {
+			RTE_LOG(ERR, EFD,
+					"Fatal: No room remaining for insert into "
+					"chunk %u group %u bin %u\n",
+					*chunk_id,
+					current_group_id, *bin_id);
+			return RTE_EFD_UPDATE_FAILED;
+		}
+
+		if (unlikely(current_group->num_rules ==
+				(EFD_MAX_GROUP_NUM_RULES - 1))) {
+			RTE_LOG(INFO, EFD, "Warn: Insert into last "
+					"available slot in chunk %u "
+					"group %u bin %u\n", *chunk_id,
+					current_group_id, *bin_id);
+			status = RTE_EFD_UPDATE_WARN_GROUP_FULL;
+		}
+
+		if (rte_ring_sc_dequeue(table->free_slots, &slot_id) != 0)
+			return RTE_EFD_UPDATE_FAILED;
+
+		new_k = RTE_PTR_ADD(table->keys, (uintptr_t) slot_id *
+					table->key_len);
+		rte_prefetch0(new_k);
+		new_idx = (uint32_t) ((uintptr_t) slot_id);
+
+		rte_memcpy(EFD_KEY(new_idx, table), key, table->key_len);
+		current_group->key_idx[current_group->num_rules] = new_idx;
+		current_group->value[current_group->num_rules] = value;
+		current_group->bin_id[current_group->num_rules] = *bin_id;
+		current_group->num_rules++;
+		table->num_rules++;
+		bin_size++;
+	} else {
+		uint32_t last = current_group->num_rules - 1;
+		/* Swap the key with the last key inserted*/
+		current_group->key_idx[key_changed_index] =
+				current_group->key_idx[last];
+		current_group->value[key_changed_index] =
+				current_group->value[last];
+		current_group->bin_id[key_changed_index] =
+				current_group->bin_id[last];
+
+		/*
+		 * Key to be updated will always be available
+		 * at the end of the group
+		 */
+		current_group->key_idx[last] = key_idx_previous;
+		current_group->value[last] = value;
+		current_group->bin_id[last] = *bin_id;
+	}
+
+	*new_bin_choice = current_choice;
+	*group_id = current_group_id;
+	new_group = current_group;
+
+	/* Group need to be rebalanced when it starts to get loaded */
+	if (current_group->num_rules > EFD_MIN_BALANCED_NUM_RULES) {
+
+		/*
+		 * Subtract the number of entries in the bin from
+		 * the original group
+		 */
+		current_group->num_rules -= bin_size;
+
+		/*
+		 * Figure out which of the available groups that this bin
+		 * can map to is the smallest (using the current group
+		 * as baseline)
+		 */
+		uint8_t smallest_choice = current_choice;
+		uint8_t smallest_size = current_group->num_rules;
+		uint32_t smallest_group_id = current_group_id;
+		unsigned char choice;
+
+		for (choice = 0; choice < EFD_CHUNK_NUM_BIN_TO_GROUP_SETS;
+				choice++) {
+			uint32_t test_group_id =
+					efd_bin_to_group[choice][*bin_id];
+			uint32_t num_rules =
+					chunk->group_rules[test_group_id].num_rules;
+			if (num_rules < smallest_size) {
+				smallest_choice = choice;
+				smallest_size = num_rules;
+				smallest_group_id = test_group_id;
+			}
+		}
+
+		*new_bin_choice = smallest_choice;
+		*group_id = smallest_group_id;
+		new_group = &chunk->group_rules[smallest_group_id];
+		current_group->num_rules += bin_size;
+
+	}
+
+	uint8_t choice = 0;
+	for (;;) {
+		if (current_group != new_group &&
+				new_group->num_rules + bin_size >
+					EFD_MAX_GROUP_NUM_RULES) {
+			RTE_LOG(DEBUG, EFD,
+					"Unable to move_groups to dest group "
+					"containing %u entries."
+					"bin_size:%u choice:%02x\n",
+					new_group->num_rules, bin_size,
+					choice - 1);
+			goto next_choice;
+		}
+		move_groups(*bin_id, bin_size, new_group, current_group);
+		/*
+		 * Recompute the hash function for the modified group,
+		 * and return it to the caller
+		 */
+		ret = efd_search_hash(table, new_group, entry);
+
+		if (!ret)
+			return status;
+
+		RTE_LOG(DEBUG, EFD,
+				"Failed to find perfect hash for group "
+				"containing %u entries. bin_size:%u choice:%02x\n",
+				new_group->num_rules, bin_size, choice - 1);
+		/* Restore groups modified to their previous state */
+		revert_groups(current_group, new_group, bin_size);
+
+next_choice:
+		if (choice == EFD_CHUNK_NUM_BIN_TO_GROUP_SETS)
+			break;
+		*new_bin_choice = choice;
+		*group_id = efd_bin_to_group[choice][*bin_id];
+		new_group = &chunk->group_rules[*group_id];
+		choice++;
+	}
+
+	if (!found) {
+		current_group->num_rules--;
+		table->num_rules--;
+	} else
+		current_group->value[current_group->num_rules - 1] =
+			key_changed_previous_value;
+	return RTE_EFD_UPDATE_FAILED;
+}
+
+int
+rte_efd_update(struct rte_efd_table * const table, const unsigned int socket_id,
+		const void *key, const efd_value_t value)
+{
+	uint32_t chunk_id = 0, group_id = 0, bin_id = 0;
+	uint8_t new_bin_choice = 0;
+	struct efd_online_group_entry entry;
+
+	int status = efd_compute_update(table, socket_id, key, value,
+			&chunk_id, &group_id, &bin_id,
+			&new_bin_choice, &entry);
+
+	if (status == RTE_EFD_UPDATE_NO_CHANGE)
+		return EXIT_SUCCESS;
+
+	if (status == RTE_EFD_UPDATE_FAILED)
+		return status;
+
+	efd_apply_update(table, socket_id, chunk_id, group_id, bin_id,
+			new_bin_choice, &entry);
+	return status;
+}
+
+int
+rte_efd_delete(struct rte_efd_table * const table, const unsigned int socket_id,
+		const void *key, efd_value_t * const prev_value)
+{
+	unsigned int i;
+	uint32_t chunk_id, bin_id;
+	uint8_t not_found = 1;
+
+	efd_compute_ids(table, key, &chunk_id, &bin_id);
+
+	struct efd_offline_chunk_rules * const chunk =
+			&table->offline_chunks[chunk_id];
+
+	uint8_t current_choice = efd_get_choice(table, socket_id,
+			chunk_id, bin_id);
+	uint32_t current_group_id = efd_bin_to_group[current_choice][bin_id];
+	struct efd_offline_group_rules * const current_group =
+			&chunk->group_rules[current_group_id];
+
+	/*
+	 * Search the current group for the specified key.
+	 * If it exists, remove it and re-pack the other values
+	 */
+	for (i = 0; i < current_group->num_rules; i++) {
+		if (not_found) {
+			/* Found key that needs to be removed */
+			if (memcmp(EFD_KEY(current_group->key_idx[i], table),
+					key, table->key_len) == 0) {
+				/* Store previous value if requested by caller */
+				if (prev_value != NULL)
+					*prev_value = current_group->value[i];
+
+				not_found = 0;
+				rte_ring_sp_enqueue(table->free_slots,
+					(void *)((uintptr_t)current_group->key_idx[i]));
+			}
+		} else {
+			/*
+			 * If the desired key has been found,
+			 * need to shift other values up one
+			 */
+
+			/* Need to shift this entry back up one index */
+			current_group->key_idx[i - 1] = current_group->key_idx[i];
+			current_group->value[i - 1] = current_group->value[i];
+			current_group->bin_id[i - 1] = current_group->bin_id[i];
+		}
+	}
+
+	if (not_found == 0) {
+		table->num_rules--;
+		current_group->num_rules--;
+	}
+
+	return not_found;
+}
+
+
+#if (RTE_EFD_VALUE_NUM_BITS == 8 || RTE_EFD_VALUE_NUM_BITS == 16 || \
+	RTE_EFD_VALUE_NUM_BITS == 24 || RTE_EFD_VALUE_NUM_BITS == 32)
+#define EFD_LOAD_SI128(val) _mm_load_si128(val)
+#else
+#define EFD_LOAD_SI128(val) _mm_lddqu_si128(val)
+#endif
+
+static inline efd_value_t
+efd_lookup_internal(const struct efd_online_group_entry * const group,
+		const uint32_t hash_val_a, const uint32_t hash_val_b,
+		enum rte_efd_compare_function cmp_fn)
+{
+	efd_value_t value = 0;
+	uint32_t i;
+
+	switch (cmp_fn) {
+#ifdef RTE_MACHINE_CPUFLAG_AVX2
+	case RTE_HASH_COMPARE_AVX2:
+
+		i = 0;
+		__m256i vhash_val_a = _mm256_set1_epi32(hash_val_a);
+		__m256i vhash_val_b = _mm256_set1_epi32(hash_val_b);
+
+		for (; i < RTE_EFD_VALUE_NUM_BITS; i += 8) {
+			__m256i vhash_idx =
+					_mm256_cvtepu16_epi32(EFD_LOAD_SI128(
+					(__m128i const *) &group->hash_idx[i]));
+			__m256i vlookup_table = _mm256_cvtepu16_epi32(
+					EFD_LOAD_SI128((__m128i const *)
+					&group->lookup_table[i]));
+			__m256i vhash = _mm256_add_epi32(vhash_val_a,
+					_mm256_mullo_epi32(vhash_idx, vhash_val_b));
+			__m256i vbucket_idx = _mm256_srli_epi32(vhash,
+					EFD_LOOKUPTBL_SHIFT);
+			__m256i vresult = _mm256_srlv_epi32(vlookup_table,
+					vbucket_idx);
+
+			value |= (_mm256_movemask_ps(
+				(__m256) _mm256_slli_epi32(vresult, 31))
+				& ((1 << (RTE_EFD_VALUE_NUM_BITS - i)) - 1)) << i;
+		}
+		break;
+#endif
+	default:
+
+		i = 0;
+		for (; i < RTE_EFD_VALUE_NUM_BITS; i++) {
+			value <<= 1;
+
+			uint32_t h = hash_val_a + (hash_val_b *
+				group->hash_idx[RTE_EFD_VALUE_NUM_BITS - i - 1]);
+			uint16_t bucket_idx = h >> EFD_LOOKUPTBL_SHIFT;
+
+			value |= (group->lookup_table[
+					RTE_EFD_VALUE_NUM_BITS - i - 1] >>
+					bucket_idx) & 0x1;
+		}
+	}
+	return value;
+}
+
+efd_value_t
+rte_efd_lookup(const struct rte_efd_table * const table,
+		const unsigned int socket_id, const void *key)
+{
+	uint32_t chunk_id, group_id, bin_id;
+	uint8_t bin_choice;
+	const struct efd_online_group_entry *group;
+	const struct efd_online_chunk * const chunks = table->chunks[socket_id];
+
+	/* Determine the chunk and group location for the given key */
+	efd_compute_ids(table, key, &chunk_id, &bin_id);
+	bin_choice = efd_get_choice(table, socket_id, chunk_id, bin_id);
+	group_id = efd_bin_to_group[bin_choice][bin_id];
+	group = &chunks[chunk_id].groups[group_id];
+
+	return efd_lookup_internal(group,
+			EFD_HASHFUNCA(key, table),
+			EFD_HASHFUNCB(key, table),
+			table->cmp_fn);
+}
+
+void rte_efd_lookup_bulk(const struct rte_efd_table * const table,
+		const unsigned int socket_id, const int num_keys,
+		const void **key_list, efd_value_t * const value_list)
+{
+	int i;
+	uint32_t chunk_id_list[RTE_EFD_BURST_MAX];
+	uint32_t bin_id_list[RTE_EFD_BURST_MAX];
+	uint8_t bin_choice_list[RTE_EFD_BURST_MAX];
+	uint32_t group_id_list[RTE_EFD_BURST_MAX];
+	struct efd_online_group_entry *group;
+
+	struct efd_online_chunk *chunks = table->chunks[socket_id];
+
+	for (i = 0; i < num_keys; i++) {
+		efd_compute_ids(table, key_list[i], &chunk_id_list[i],
+				&bin_id_list[i]);
+		rte_prefetch0(&chunks[chunk_id_list[i]].bin_choice_list);
+	}
+
+	for (i = 0; i < num_keys; i++) {
+		bin_choice_list[i] = efd_get_choice(table, socket_id,
+				chunk_id_list[i], bin_id_list[i]);
+		group_id_list[i] =
+				efd_bin_to_group[bin_choice_list[i]][bin_id_list[i]];
+		group = &chunks[chunk_id_list[i]].groups[group_id_list[i]];
+		rte_prefetch0(group);
+	}
+
+	for (i = 0; i < num_keys; i++) {
+		group = &chunks[chunk_id_list[i]].groups[group_id_list[i]];
+		value_list[i] = efd_lookup_internal(group,
+				EFD_HASHFUNCA(key_list[i], table),
+				EFD_HASHFUNCB(key_list[i], table),
+				table->cmp_fn);
+	}
+}
diff --git a/lib/librte_efd/rte_efd.h b/lib/librte_efd/rte_efd.h
new file mode 100644
index 0000000..a728096
--- /dev/null
+++ b/lib/librte_efd/rte_efd.h
@@ -0,0 +1,294 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_EFD_H_
+#define _RTE_EFD_H_
+
+/**
+ * @file
+ *
+ * RTE EFD Table
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*************************************************************************
+ * User selectable constants
+ *************************************************************************/
+
+/*
+ * If possible, best lookup performance will be achieved by ensuring that
+ * the entire table fits in the L3 cache.
+ *
+ * Some formulas for calculating various sizes are listed below:
+ *
+ * # of chunks =
+ *   2 ^ (ceiling(log2((requested # of rules) /
+ *            (EFD_CHUNK_NUM_GROUPS * EFD_TARGET_GROUP_NUM_RULES))))
+ *
+ * Target # of rules = (# of chunks) * EFD_CHUNK_NUM_GROUPS *
+ *            EFD_TARGET_GROUP_NUM_RULES
+ *
+ * Group Size (in bytes) = 4 (per value bit)
+ *
+ * Table size (in bytes) = RTE_EFD_VALUE_NUM_BITS * (# of chunks) *
+ *            EFD_CHUNK_NUM_GROUPS * (group size)
+ */
+
+/**
+ * !!! This parameter should be adjusted for your application !!!
+ *
+ * This parameter adjusts the number of bits of value that can be
+ * stored in the table.
+ * For example, setting the number of bits to 3 will allow storing 8 values
+ * in the table (between 0 and 7).
+ *
+ * This number directly affects the performance of both lookups and insertion.
+ * In general, performance decreases as more bits are stored in the table.
+ *
+ * This number is directly proportional to the size of the online region
+ * used for lookups.
+ *
+ * Note that due to the way the CPU operates on memory, best lookup performance
+ * will be achieved when RTE_EFD_VALUE_NUM_BITS is a multiple of 8.
+ * These values align the hash indexes on 16-byte boundaries.
+ * The greatest performance drop is moving from 8->9 bits, 16->17 bits, etc.
+ *
+ * This value must be between 1 and 32
+ */
+#ifndef RTE_EFD_VALUE_NUM_BITS
+#define RTE_EFD_VALUE_NUM_BITS (8)
+#endif
+
+/*
+ * EFD_TARGET_GROUP_NUM_RULES:
+ *   Adjusts how many groups/chunks are allocated at table creation time
+ *   to support the requested number of rules. Higher values pack entries
+ *   more tightly in memory, resulting in a smaller memory footprint
+ *   for the online table.
+ *   This comes at the cost of lower insert/update performance.
+ *
+ * EFD_MAX_GROUP_NUM_RULES:
+ *   This adjusts the amount of offline memory allocated to store key/value
+ *   pairs for the table. The recommended numbers are upper-bounds for
+ *   this parameter
+ *   - any higher and it becomes very unlikely that a perfect hash function
+ *   can be found for that group size. This value should be at
+ *   least 40% larger than EFD_TARGET_GROUP_NUM_RULES
+ *
+ * Recommended values for various lookuptable and hashfunc sizes are:
+ *
+ *   HASH_FUNC_SIZE = 16, LOOKUPTBL_SIZE = 16:
+ *     EFD_TARGET_GROUP_NUM_RULES = 22
+ *     EFD_MAX_GROUP_NUM_RULES = 28
+ */
+#define EFD_TARGET_GROUP_NUM_RULES (22)
+#define EFD_MAX_GROUP_NUM_RULES (28LU)
+
+#define EFD_MIN_BALANCED_NUM_RULES      5
+
+/**
+ * Maximum number of keys that can be looked up in one call to efd_lookup_bulk
+ */
+#ifndef RTE_EFD_BURST_MAX
+#define RTE_EFD_BURST_MAX (32)
+#endif
+
+/** Maximum number of characters in efd name.*/
+#define RTE_EFD_NAMESIZE			32
+
+#if (RTE_EFD_VALUE_NUM_BITS > 0 && RTE_EFD_VALUE_NUM_BITS <= 8)
+typedef uint8_t efd_value_t;
+#elif (RTE_EFD_VALUE_NUM_BITS > 8 && RTE_EFD_VALUE_NUM_BITS <= 16)
+typedef uint16_t efd_value_t;
+#elif (RTE_EFD_VALUE_NUM_BITS > 16 && RTE_EFD_VALUE_NUM_BITS <= 32)
+typedef uint32_t efd_value_t;
+#else
+#error("RTE_EFD_VALUE_NUM_BITS must be in the range [1:32]")
+#endif
+
+/**
+ * Creates an EFD table with a single offline region and multiple per-socket
+ * internally-managed copies of the online table used for lookups
+ *
+ * @param name
+ *   EFD table name
+ * @param max_num_rules
+ *   Minimum number of rules the table should be sized to hold.
+ *   Will be rounded up to the next smallest valid table size
+ * @param online_cpu_socket_bitmask
+ *   Bitmask specifying which sockets should get a copy of the online table.
+ *   LSB = socket 0, etc.
+ * @param offline_cpu_socket
+ *   Identifies the socket where the offline table will be allocated
+ *   (and most efficiently accessed in the case of updates/insertions)
+ *
+ * @return
+ *   EFD table, or NULL if table allocation failed or the bitmask is invalid
+ */
+struct rte_efd_table *
+rte_efd_create(const char *name, uint32_t max_num_rules, uint32_t key_len,
+	uint8_t online_cpu_socket_bitmask, uint8_t offline_cpu_socket);
+
+/**
+ * Releases the resources from an EFD table
+ *
+ * @param table
+ *   Table to free
+ */
+void
+rte_efd_free(struct rte_efd_table *table);
+
+/**
+ * Find an existing EFD table object and return a pointer to it.
+ *
+ * @param name
+ *   Name of the EFD table as passed to rte_efd_create()
+ * @return
+ *   Pointer to EFD table or NULL if object not found
+ *   with rte_errno set appropriately. Possible rte_errno values include:
+ *    - ENOENT - value not available for return
+ */
+struct rte_efd_table*
+rte_efd_find_existing(const char *name);
+
+#define RTE_EFD_UPDATE_WARN_GROUP_FULL   (1)
+#define RTE_EFD_UPDATE_NO_CHANGE         (2)
+#define RTE_EFD_UPDATE_FAILED            (3)
+
+/**
+ * Computes an updated table entry for the supplied key/value pair.
+ * The update is then immediately applied to the provided table and
+ * all socket-local copies of the chunks are updated.
+ *
+ * @param table
+ *   EFD table to reference
+ * @param socket_id
+ *   Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param key
+ *   EFD table key to modify
+ * @param value
+ *   Value to associate with the key
+ *
+ * @return
+ *  RTE_EFD_UPDATE_WARN_GROUP_FULL
+ *     Operation is insert, and the last available space in the
+ *     key's group was just used
+ *     Future inserts may fail as groups fill up
+ *     This operation was still successful, and entry contains a valid update
+ *  RTE_EFD_UPDATE_FAILED
+ *     Either the EFD failed to find a suitable perfect hash or the group was full
+ *     This is a fatal error, and the table is now in an indeterminite state
+ *  RTE_EFD_UPDATE_NO_CHANGE
+ *     Operation resulted in no change to the table (same value already exists)
+ *  0 - success
+ */
+int
+rte_efd_update(struct rte_efd_table *table, unsigned int socket_id,
+	const void *key, efd_value_t value);
+
+/**
+ * Removes any value currently associated with the specified key from the table
+ *
+ * @param table
+ *   EFD table to reference
+ * @param socket_id
+ *   Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param key
+ *   EFD table key to delete
+ * @param prev_value
+ *   If not NULL, will store the previous value here before deleting it
+ *
+ * @return
+ *   0 - successfully found and deleted the key
+ *   nonzero otherwise
+ */
+int
+rte_efd_delete(struct rte_efd_table *table, unsigned int socket_id,
+	const void *key, efd_value_t *prev_value);
+
+/**
+ * Looks up the value associated with a key
+ *
+ * NOTE: Lookups will *always* succeed - this is a property of
+ * using a perfect hash table.
+ * If the specified key was never inserted, a pseudorandom answer will be returned.
+ * There is no way to know based on the lookup if the key was ever inserted
+ * originally, so this must be tracked elsewhere.
+ *
+ * @param table
+ *   EFD table to reference
+ * @param socket_id
+ *   Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param key
+ *   EFD table key to look up
+ *
+ * @return
+ *   Value associated with the key, or random junk if they key was never inserted
+ */
+efd_value_t
+rte_efd_lookup(const struct rte_efd_table *table, unsigned int socket_id,
+		const void *key);
+
+/**
+ * Looks up the value associated with several keys.
+ *
+ * NOTE: Lookups will *always* succeed - this is a property of
+ * using a perfect hash table.
+ * If the specified key was never inserted, a pseudorandom answer will be returned.
+ * There is no way to know based on the lookup if the key was ever inserted
+ * originally, so this must be tracked elsewhere.
+ *
+ * @param table
+ *   EFD table to reference
+ * @param socket_id
+ *   Socket ID to use to lookup existing value (ideally caller's socket id)
+ * @param num_keys
+ *   Number of keys in the key_list array, must be less than RTE_EFD_BURST_MAX
+ * @param key_list
+ *   Array of num_keys pointers which point to keys to look up
+ * @param value_list
+ *   Array of size num_keys where lookup values will be stored
+ */
+void
+rte_efd_lookup_bulk(const struct rte_efd_table *table, unsigned int socket_id,
+		int num_keys, const void **key_list,
+		efd_value_t *value_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_EFD_H_ */
diff --git a/lib/librte_efd/rte_efd_version.map b/lib/librte_efd/rte_efd_version.map
new file mode 100644
index 0000000..91810b3
--- /dev/null
+++ b/lib/librte_efd/rte_efd_version.map
@@ -0,0 +1,12 @@
+DPDK_17.02 {
+	global:
+
+	rte_efd_create;
+	rte_efd_delete;
+	rte_efd_free;
+	rte_efd_lookup;
+	rte_efd_lookup_bulk;
+	rte_efd_update;
+
+	local: *;
+};
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index f75f0e2..ed1e68a 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -1,6 +1,6 @@
 #   BSD LICENSE
 #
-#   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+#   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
 #   Copyright(c) 2014-2015 6WIND S.A.
 #   All rights reserved.
 #
@@ -86,6 +86,7 @@ _LDLIBS-y += --whole-archive
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
 _LDLIBS-$(CONFIG_RTE_LIBRTE_HASH)           += -lrte_hash
+_LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
 _LDLIBS-$(CONFIG_RTE_LIBRTE_VHOST)          += -lrte_vhost
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KVARGS)         += -lrte_kvargs
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 0/5] Elastic Flow Distributor
From: Pablo de Lara @ 2017-01-15 12:04 UTC (permalink / raw)
  To: dev; +Cc: Pablo de Lara
In-Reply-To: <1484259360-198276-1-git-send-email-pablo.de.lara.guarch@intel.com>

EFD is a distributor library that uses perfect hashing to determine a
target/value for a given incoming flow key. It has the following advantages:
first, because it uses perfect hashing it does not store the key itself and
hence lookup performance is not dependent on the key size. Second, the
target/value can be any arbitrary value hence the system designer and/or
operator can better optimize service rates and inter-cluster network traffic
locating.  Third, since the storage requirement is much smaller than a
hash-based flow table (i.e. better fit for CPU cache), EFD can scale to millions
of flow keys. Finally, with current optimized library implementation performance
is fully scalable with number of CPU cores.

The basic idea of EFD is when a given key is to be inserted, a family of hash
functions is searched until the correct hash function that maps the input key to
the correct value is found. However, rather than explicitly storing all keys and
their associated values, EFD stores only indices of hash functions that map keys
to values, and thereby consumes much less space than conventional  flow-based
tables. The lookup operation is very simple, similar to computational-based
scheme, given an input key the lookup operation is reduced to hashing that key
with the correct hash function.

Intuitively, finding a hash function that maps each of a large number (millions)
of input keys to the correct output value is effectively impossible, as a result
EFD, breaks the problem into smaller pieces (divide and conquer). EFD divides
the entire input key set into many small groups. Each group consists of
approximately 20-28 keys (a configurable parameter for the library), then, for
each small group, a brute force search to find a hash function that produces the
correct outputs for each key in the group.
It should be mentioned that since in the online lookup table for EFD doesn’t
store the key itself, the size of the EFD table is independent of the key size
and hence EFD lookup performance which is almost constant irrespective of the
length of the key which is a highly desirable feature especially for longer
keys.

Library code is included in the patch, plus an sample application that shows
how the library can be used.

RFC for this library was already sent:
http://dpdk.org/ml/archives/dev/2016-October/049238.html

For more information on the library, check out the following document: 
https://github.com/pablodelara/perfect_hash_flow_distributor/blob/master/EFD_description.pdf

Changes in v4:

- Added References section

Changes in v3:

- Fixed SVG files
- Fixed copyright dates
- Reformatted parts of documentation


Changes in v2:

- Added documentation for library and sample app
- Fixed checkpatch errors/warnings
- Added functional and performance tests
- Made key size variable at runtime
- Made code multi-architecture compatible at runtime

Pablo de Lara (5):
  efd: new Elastic Flow Distributor library
  app/test: add EFD functional and perf tests
  examples/flow_distributor: sample app to demonstrate EFD usage
  doc: add EFD library section in Programmers guide
  doc: add flow distributor guide

 MAINTAINERS                                       |    9 +
 app/test/Makefile                                 |    5 +-
 app/test/test_efd.c                               |  494 ++++++++
 app/test/test_efd_perf.c                          |  407 +++++++
 config/common_base                                |    5 +
 doc/api/doxy-api-index.md                         |    3 +-
 doc/api/doxy-api.conf                             |    1 +
 doc/api/examples.dox                              |    4 +
 doc/guides/prog_guide/efd_lib.rst                 |  438 +++++++
 doc/guides/prog_guide/img/efd_i1.svg              |  130 ++
 doc/guides/prog_guide/img/efd_i10.svg             |  384 ++++++
 doc/guides/prog_guide/img/efd_i11.svg             |  319 +++++
 doc/guides/prog_guide/img/efd_i12.svg             | 1008 +++++++++++++++
 doc/guides/prog_guide/img/efd_i2.svg              |  280 +++++
 doc/guides/prog_guide/img/efd_i3.svg              |  634 ++++++++++
 doc/guides/prog_guide/img/efd_i4.svg              |  203 +++
 doc/guides/prog_guide/img/efd_i5.svg              |  183 +++
 doc/guides/prog_guide/img/efd_i6.svg              | 1254 +++++++++++++++++++
 doc/guides/prog_guide/img/efd_i7.svg              |  790 ++++++++++++
 doc/guides/prog_guide/img/efd_i8.svg              |  182 +++
 doc/guides/prog_guide/img/efd_i9.svg              |  390 ++++++
 doc/guides/prog_guide/index.rst                   |   23 +
 doc/guides/rel_notes/release_17_02.rst            |   15 +
 doc/guides/sample_app_ug/flow_distributor.rst     |  494 ++++++++
 doc/guides/sample_app_ug/img/flow_distributor.svg | 1254 +++++++++++++++++++
 doc/guides/sample_app_ug/index.rst                |    1 +
 examples/Makefile                                 |    1 +
 examples/flow_distributor/Makefile                |   44 +
 examples/flow_distributor/distributor/Makefile    |   57 +
 examples/flow_distributor/distributor/args.c      |  200 +++
 examples/flow_distributor/distributor/args.h      |   39 +
 examples/flow_distributor/distributor/init.c      |  371 ++++++
 examples/flow_distributor/distributor/init.h      |   76 ++
 examples/flow_distributor/distributor/main.c      |  362 ++++++
 examples/flow_distributor/node/Makefile           |   48 +
 examples/flow_distributor/node/node.c             |  417 +++++++
 examples/flow_distributor/shared/common.h         |   99 ++
 lib/Makefile                                      |    3 +-
 lib/librte_eal/common/include/rte_log.h           |    3 +-
 lib/librte_efd/Makefile                           |   56 +
 lib/librte_efd/rte_efd.c                          | 1354 +++++++++++++++++++++
 lib/librte_efd/rte_efd.h                          |  294 +++++
 lib/librte_efd/rte_efd_version.map                |   12 +
 mk/rte.app.mk                                     |    3 +-
 44 files changed, 12344 insertions(+), 5 deletions(-)
 create mode 100644 app/test/test_efd.c
 create mode 100644 app/test/test_efd_perf.c
 create mode 100644 doc/guides/prog_guide/efd_lib.rst
 create mode 100644 doc/guides/prog_guide/img/efd_i1.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i10.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i11.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i12.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i2.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i3.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i4.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i5.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i6.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i7.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i8.svg
 create mode 100644 doc/guides/prog_guide/img/efd_i9.svg
 create mode 100644 doc/guides/sample_app_ug/flow_distributor.rst
 create mode 100644 doc/guides/sample_app_ug/img/flow_distributor.svg
 create mode 100644 examples/flow_distributor/Makefile
 create mode 100644 examples/flow_distributor/distributor/Makefile
 create mode 100644 examples/flow_distributor/distributor/args.c
 create mode 100644 examples/flow_distributor/distributor/args.h
 create mode 100644 examples/flow_distributor/distributor/init.c
 create mode 100644 examples/flow_distributor/distributor/init.h
 create mode 100644 examples/flow_distributor/distributor/main.c
 create mode 100644 examples/flow_distributor/node/Makefile
 create mode 100644 examples/flow_distributor/node/node.c
 create mode 100644 examples/flow_distributor/shared/common.h
 create mode 100644 lib/librte_efd/Makefile
 create mode 100644 lib/librte_efd/rte_efd.c
 create mode 100644 lib/librte_efd/rte_efd.h
 create mode 100644 lib/librte_efd/rte_efd_version.map

-- 
2.7.4

^ permalink raw reply

* Re: net/i40e: TX descriptor is not done
From: Gregory Etelson @ 2017-01-15  9:08 UTC (permalink / raw)
  To: dev; +Cc: users
In-Reply-To: <36207324.ubtIbUzSEP@polaris>

Update

The fault is relevant to VF only
DPDK over i40e PF functions correctly

Regards,
Gregory

On Thu, Jan 12, 2017 at 11:14 AM, Gregory Etelson <gregory@weka.io> wrote:

> Hello,
>
>
>
> I have a standalone utility that implements
>
> ICMP ping over DPDK.
>
> When I run the utility with i40e PMD over VF it stacks with the following
> output and never recovers.
>
> The last reported  tx_tail value was 4095. TX queue length in that run was
> 4096
>
> The fault was reproduced on several Dell PowerEdge R630 servers
>
>
>
> My environment:
>
> DPDK 16.11 + 6809e62e555c7b36d248a0e8086e3c3bbbb87a37 (net/i40e: enable
> VF MTU change)
>
> i40e: version: 1.5.25, firmware-version: 5.04 0x80002537 0.0.0
>
> Regards,
>
> Gregory
>
> PMD: i40e_xmit_pkts(): port_id=0 queue_id=0 tx_tail=4094 nb_tx=1
>
> PMD: i40e_rxd_to_vlan_tci(): Mbuf vlan_tci: 0, vlan_tci_outer: 0
>
> PMD: i40e_rxd_to_vlan_tci(): Mbuf vlan_tci: 0, vlan_tci_outer: 0
>
> PMD: i40e_rxd_to_vlan_tci(): Mbuf vlan_tci: 0, vlan_tci_outer: 0
>
> IN[0] ETH[00:50:56:88:A4:F9->46:51:31:29:89:82 proto 0800]
> IP[10.222.35.7->10.222.131.1 len 498 proto 01] ICMP[REPLY seq 4090]
>
> IN[0] ETH[00:50:56:88:A4:F9->46:51:31:29:89:82 proto 0800]
> IP[10.222.35.7->10.222.131.1 len 498 proto 01] ICMP[REPLY seq 4091]
>
> IN[0] ETH[00:50:56:88:A4:F9->46:51:31:29:89:82 proto 0800]
> IP[10.222.35.7->10.222.131.1 len 498 proto 01] ICMP[REPLY seq 4092]
>
> OUT[0] ETH[46:51:31:29:89:82->00:50:56:88:A4:F9 proto 0800]
> IP[10.222.131.1->10.222.35.7 len 498 proto 01] ICMP[REQUEST seq 4093]
>
> PMD: i40e_xmit_cleanup(): TX descriptor 31 is not done (port=0 queue=0)
>
> PMD: i40e_xmit_pkts(): mbuf: 0x7efebc07c500, TDD[4094]:
>
> buf_dma_addr: 0x81de7c600;
>
> td_cmd: 0x4;
>
> td_offset: 0;
>
> td_len: 512;
>
> td_tag: 0;
>
>
>
>
>
> PMD: i40e_xmit_pkts(): port_id=0 queue_id=0 tx_tail=4095 nb_tx=1
>
> PMD: i40e_rxd_to_vlan_tci(): Mbuf vlan_tci: 0, vlan_tci_outer: 0
>
> IN[0] ETH[00:50:56:88:A4:F9->46:51:31:29:89:82 proto 0800]
> IP[10.222.35.7->10.222.131.1 len 498 proto 01] ICMP[REPLY seq 4093]
>
> OUT[0] ETH[46:51:31:29:89:82->00:50:56:88:A4:F9 proto 0800]
> IP[10.222.131.1->10.222.35.7 len 498 proto 01] ICMP[REQUEST seq 4094]
>
> PMD: i40e_xmit_cleanup(): TX descriptor 31 is not done (port=0 queue=0)
>
> PMD: i40e_xmit_cleanup(): TX descriptor 31 is not done (port=0 queue=0)
>
> PMD: i40e_xmit_cleanup(): TX descriptor 31 is not done (port=0 queue=0)
>

^ permalink raw reply

* Re: [PATCH v6 00/18] net/ixgbe: Consistent filter API
From: Lu, Wenzhuo @ 2017-01-15  2:44 UTC (permalink / raw)
  To: Zhao1, Wei, dev@dpdk.org
In-Reply-To: <44b6090b-7a19-4f6d-e408-b73656c10283@intel.com>

Hi Ferruh,

> Series applied to dpdk-next-net/master, thanks.
Great, thanks for applying this patch set!

^ permalink raw reply

* Re: [PATCH v5] app/testpmd: supported offload capabilities query
From: Yang, Qiming @ 2017-01-14  3:06 UTC (permalink / raw)
  To: De Lara Guarch, Pablo, dev@dpdk.org; +Cc: Lu, Wenzhuo
In-Reply-To: <E115CCD9D858EF4F90C690B0DCB4D897476C3F1B@IRSMSX108.ger.corp.intel.com>

HI, Pablo

> ...
> 
> > +
> > +	if (dev_info.rx_offload_capa & DEV_RX_OFFLOAD_TCP_CKSUM) {
> > +		printf("RX TCP checksum:               ");
> > +		if (dev->data->dev_conf.rxmode.hw_ip_checksum)
> > +			printf("on\n");
> > +		else
> > +			printf("off\n");
> > +	}
> > +
> > +	if (dev_info.rx_offload_capa &
> > DEV_RX_OFFLOAD_OUTER_IPV4_CKSUM)
> > +		printf("RX Outer IPv4 checksum:        ");
> 
> Missing on/off?
> Qiming: I didn't find any switch for this feature in DPDK now. I'll 
> fix the format problem. Thank you.

I just saw your v6. So, what does this mean? If the device is capable of doing this, but there is no way to enable/disable it, it would mean that it will be enabled or disabled by default, right?

I see that X550 NIC is the only one capable of this. Maybe Wenzhuo can help on clarifying this.

I would say that, if it is enabled when it is capable, then you should print "on", But printing only "RX Outer IPv4 checksum" does not give any information (when all the rest of the capabilities show on/off).

Qiming: Ok, I'll check with Wenzhuo, Thank you for your advice for the unsuitable print in this place .

Thanks,
Pablo
 

^ permalink raw reply

* Re: [PATCH v5 00/29] Support VFD and DPDK PF + kernel VF on i40e
From: JOSHI, KAUSTUBH  (KAUSTUBH) @ 2017-01-13 23:49 UTC (permalink / raw)
  To: John Fastabend
  Cc: Chen, Jing D, Vincent JARDIN, DANIELS, EDWARD S (EDWARD),
	dev@dpdk.org, ZELEZNIAK, ALEX, Fastabend, John R
In-Reply-To: <58795CE3.4080403@gmail.com>

John,

Thanks for comments. To answer a specific question you raised:

> Open question for me on this though is the PF in these SRIOV cases
> ever being used by DPDK datapath or is it just being leveraged for
> configuration with primarily control traffic?

In our case, the answer is yes. We intend to use the PF through DPDK to house a high speed packet mirror function for capturing packet traces from VFs (for ops etc). 

However, in theory, this *can* be done through an extra VF as well, so I didn't bring up that particular point as being a deciding factor in this debate. 

> On Jan 13, 2017, at 6:04 PM, John Fastabend <john.fastabend@gmail.com> wrote:
> 
> Open question for me on this though is the PF in these SRIOV cases
> ever being used by DPDK datapath or is it just being leveraged for
> configuration with primarily control traffic?

^ permalink raw reply

* Re: [PATCH v8 00/25] Support VFD on i40e
From: John Fastabend @ 2017-01-13 23:10 UTC (permalink / raw)
  To: JOSHI, KAUSTUBH (KAUSTUBH), Vincent Jardin
  Cc: Zhang, Helin, Lu, Wenzhuo, dev@dpdk.org,
	DANIELS, EDWARD S (EDWARD), ZELEZNIAK, ALEX
In-Reply-To: <194F5517-88BD-4BDB-B0FC-A6AC6B08F588@research.att.com>

On 17-01-11 06:51 AM, JOSHI, KAUSTUBH  (KAUSTUBH) wrote:
> Also, the kernel drivers have no concept of passing VF messages to upstream "decision making” (or policy enforcement) software like VFd.
> 
> On Jan 11, 2017, at 9:49 AM, Kaustubh Joshi <kaustubh@research.att.com<mailto:kaustubh@research.att.com>> wrote:
> 
> When Alex from our team started working on Niantic last year, the following were the list of gaps in the kernel drivers we had a need to fill:
> 

Thanks for the list its nice to see concrete examples. I can
provide latest upstream status for what its worth.

> Direct traffic to VF based on more than one outer VLAN tags

Kernel supports this but ixgbe/i40e drivers need to pull in code to
enable it.

> Optionally strip on ingress (to PF) and insert on egress VLAN tag

This is just a PVID per VF right? This should be working now on
ixgbe I would have to check though.

> Disable/enable MAC and VLAN anti spoofing separately

Needs kernel patch.

> Mirror traffic from one VF to the other

Kernel supports this but driver is missing feature.

> Enable/Disable local switching per VF

Local switching? I'm guessing this means you want all traffic to
go to network regardless of MAC/etc. A sort of per port VEPA mode?

> Collect statistics per VF pkts/octets in/out

Under development.

> Enable/disable Mcast/unknown unicast per VF

Under development.

> Manage up to 8 TC per VF with one strict priority queue
> Manage per VF per TC bandwidth allocations

Needs kernel patch.

> Manage LACP status visibility to the VFs (for NIC teaming using SRIOV)

I've not even thought to do this yet so its a good catch.

> 
> Most of these are VF management functions, and there is no standardized way to do VF management in the kernel drivers. Besides, most of the use-cases around SRIOV need DPDK in the VF anyway (so the target communities are aligned) and the PF DPDK driver for ixgbe already existed, so it made sense to add them there - no forking of the PF driver was involved and there is no additional duplicate code.
> 

So I wont argue against we already have DPDK and VFs are DPDK and
updating is a problem per other email. But I think we can at least
get kernel support for the above.

Thanks,
John

> Cheers
> 
> KJ
> 
> 
> On Jan 11, 2017, at 6:03 AM, Vincent Jardin <vincent.jardin@6wind.com<mailto:vincent.jardin@6wind.com>> wrote:
> 
> Please can you list the gaps of the Kernel API?
> 
> Thank you,
> Vincent
> 
> 
> Le 11 janvier 2017 3:59:45 AM "JOSHI, KAUSTUBH  (KAUSTUBH)" <kaustubh@research.att.com<mailto:kaustubh@research.att.com>> a écrit :
> 
> Hi Vincent,
> 
> Greetings! Jumping into this debate a bit late, but let me share our point of view based on how we are using this code within AT&T for our NFV cloud.
> 
> Actually, we first started with trying to do the configuration within the kernel drivers as you suggest, but quickly realized that besides the practical problem of kernel upstreaming being a much more arduous road (which can be overcome), the bigger problem was that there is no standardization in the configuration interfaces for the NICs in the kernel community. So different drivers do things differently and expose different settings, and no forum exists to drive towards such standardization. This was leading to vendors have to maintain patched versions of drivers for doing PF configuration, which is not a desirable situation.
> 
> So, to build a portable (to multiple NICs) SRIOV VF manager like VFd, DPDK seemed like a good a forum with some hope for driving towards a standard set of interfaces and without having to worry about a lot of legacy baggage and old hardware. Especially since DPDK already takes on the role of configuring NICs for the data plane functions anyway - both PF and VF drivers will have to be included for data plane usage anyway - we viewed that adding VF config options will not cause any forking, but simply flush out the DPDK drivers and their interfaces to be more complete. These APIs could be optional, so new vendors aren’t obligated to add them.
> 
> Furthermore, allowing VF config using the DPDK PF driver also has the side benefit of allowing a complete SRIOV system (both VF and PF) to be built entirely with DPDK, also making version alignment easier.
> 
> We started with Niantic, which already had PF and VF drivers, and things have worked out very well with it. However, we would like VFd to be a multi-NIC vendor agnostic VF management tool, which is why we’ve been asking for making the PF config APIs richer.
> 
> Regards
> 
> KJ
> 
> 
> On Jan 10, 2017, at 3:23 PM, Vincent Jardin <vincent.jardin@6wind.com<mailto:vincent.jardin@6wind.com>> wrote:
> 
> Nope. First one needs to assess if DPDK should be intensively used to become a PF knowing Linux can do the jobs. Linux kernel community does not like the forking of Kernel drivers, I tend to agree that we should not keep duplicating options that can be solved with the Linux kernel.
> 
> Best regards,
> Vincent
> 
> 
> 
> 
> 
> 
> 

^ permalink raw reply

* Re: [PATCH v5 00/29] Support VFD and DPDK PF + kernel VF on i40e
From: John Fastabend @ 2017-01-13 23:04 UTC (permalink / raw)
  To: Chen, Jing D, Vincent JARDIN, Scott Daniels, dev@dpdk.org
  Cc: kaustubh@research.att.com, az5157@att.com, Fastabend, John R
In-Reply-To: <4341B239C0EFF9468EE453F9E9F4604D3C5D9598@shsmsx102.ccr.corp.intel.com>

On 17-01-11 07:47 PM, Chen, Jing D wrote:
> Hi, Vincent,
> 
>> -----Original Message-----
>> From: Vincent JARDIN [mailto:vincent.jardin@6wind.com]
>> Sent: Tuesday, January 10, 2017 9:30 PM
>> To: Scott Daniels <daniels@research.att.com>; dev@dpdk.org
>> Cc: kaustubh@research.att.com; az5157@att.com; Chen, Jing D
>> <jing.d.chen@intel.com>
>> Subject: Re: [dpdk-dev] [PATCH v5 00/29] Support VFD and DPDK PF + kernel VF on
>> i40e
>>
>> Hi Scott,
>>
>> Le 04/01/2017 à 22:09, Scott Daniels a écrit :
>>>  With holidays we are a bit late with our thoughts, but would like to
>>>  toss them into the mix.
>>
>> Same, I hope I am not missing emails. I do appreciate your arguments, it
>> provides lot of light. See below,
>>
>>>  The original NAK is understandable, however having the ability to
>>>  configure the PF via DPDK is advantageous for several reasons:
>>>
>>>  1) While some functions may be duplicated and/or available from the kernel
>>>  driver, it is often not possible to introduce new kernel drivers into
>>>  production without a large amount of additional testing of the entire
>>>  platform which can cause a significant delay when introducing a DPDK based
>>>  product.  If the PF control is a part of the DPDK environment, then only
>>>  the application needs to pass the operational testing before deployment; a
>>>  much more simple task.
>>
>> So we agree: you confirm that your foresee the benefits of using DPDK to
>> *bypass the role of the Kernel being the PF* of reference for the
>> hypervisor.
>>
>>>  2) If the driver changes are upstreamed into the kernel proper, the
>>>  difficulty of operational readiness testing increases as a new kernel is
>>>  introduced, and further undermines the ability to quickly and easily
>>>  release a DPDK based application into production.  While the application
>>>  may eventually fall back on driver and/or kernel support, this could be
>>>  years away.
>>
>> I do agree with the benefits of the agilities and the upsides it brings.
>> But they are other options to get the same agility without creating a
>> fragmentation of PFs.
>>
>> For example, you do not have to update the whole kernel, you can just
>> update the PF kernel module that can be upgraded with the latest needed
>> features.
>>
>>>  3) As DPDK is being used to configure the NIC, it just seems to make
>>>  sense, for consistency, that the configuration capabilities should include
>>>  the ability to configure the PF as is proposed.
>>
>>  From this perspective, the kernel modules are fine for the PF: most
>> kernels of hypervisors support it without the need to upgrade their kernels.
>>
>> To summarize, I understand that you need a flexible way to upgrade PF
>> features without touching/changing the kernel. So let's check the kernel
>> module option? VFD brings some interesting capabilities, could it be a
>> way to push and stimulate the i40e features instead of using DPDK?
>>
>>    https://sourceforge.net/projects/e1000/files/i40e%20stable/
>> for instance could be better stimulated.
> 

I figured I would weight in with my $0.02.

> May I ask what if DPDK VF need a new extension function from PF?
> Then, we'll have to build kernel community expertise and submit
> patch to Linux PF and wait for merge.

Correct, but it is the same story on DPDK repository as well. Either
way patch must be generated, tested, submitted and wait until it is
merged. At this point I think even the code is very similar sans
style and naming conventions for these types of features.

> Your proposal indicates DPDK community submitters will have to
> ask Linux community to authorize if we'll have any requirements
> in DPDK VF.

I don't think this is really much of a problem in most cases the same
company maintains both DPDK and Linux kernel drivers so hopefully
everyone is aligned.

> Comparing fragmentation, the extra dependency worry me most.
> Can you imagine how long it will be for any VF features gets
> ready?  
> 
> 

The Linux kernel releases every 6-8 weeks roughly depending on the
release. In networking stack land features must be submitted for
that kernel release before the first RC release.

So although there may be benefits to pushing these features into
DPDK vs Linux kernel I don't think actually getting the code in
the kernel is one of them.

To the point above about out of tree drivers. Most vendors do keep
out of tree drivers with the latest features and fixes that can be
supported on older kernels. Unfortunately, many of the interfaces
like the type mentioned here would require kernel updates outside
the drivers to add message handlers and driver callbacks for the
new config messages. So it may not be viable at least unless we
restructure some of the kernel to use out of tree drivers in the
short term.

So I'm sympathetic to argument (1) here. If _not_ updating the
kernel is a hard requirement then we are a bit stuck from using
existing/future Linux features. Further this is sort of a rolling
problem as well. Today we are missing feature X and we add it and
tomorrow we are missing a new feature Y. So if the feature list
is not stable with deployment of new kernels its a bit hard to
see how the Linux kernel PF model works without above mentioned
restructuring.  FWIW traditionally the Linux vendors handled this
backporting for its customers and provide kernel updates using
apt-get/dnf/whatever along with all the sanity testing to ensure
they didn't break something along the way. We are slowly getting
over this with work in BPF and other subsystems but the VF
configuration is still very much hard coded for the moment. I might
try to fix this though as a result of this thread.

Open question for me on this though is the PF in these SRIOV cases
ever being used by DPDK datapath or is it just being leveraged for
configuration with primarily control traffic?

So my take away from this is arguments about its not available in Linux
kernel are not really good ones because its easy enough to add and
get in stable kernels. But if kernel updates are a challenge then
we either need a more robust kernel backporting and out of tree
driver effort _or_ push them directly into PF driver of DPDK. I think
longterm we can fix this in the kernel with some restructuring and
better module updates but in the short term DPDK PMD updates seem
to be the only option.

I do think though if this gets pushed into DPDK PMD we shouldn't let
the PF Linux driver simply not support the new mailbox messages. This
will create one more dependency then I care to worry about. VF<->PF
mailbox should work with any combination of VF and PF as long as
version requirements are met. I think vendor maintainers can
manage this.

I'll comment on the specific features in another email because it
is helpful to have a list in public mailing list with features folks
are interested in. Any chance we could get a page up with a list
of all these and where/when we expect support for them?

Thanks,
John

john.r.fastabend@intel.com

^ permalink raw reply

* Re: [PATCH v3 3/8] crypto/armv8: add PMD optimized for ARMv8 processors
From: Zbigniew Bodek @ 2017-01-13 19:15 UTC (permalink / raw)
  To: Hemant Agrawal; +Cc: dev, pablo.de.lara.guarch, Jerin Jacob
In-Reply-To: <08761663-35b6-85de-3c55-c4cc52537986@nxp.com>



On 13.01.2017 08:57, Hemant Agrawal wrote:
> On 1/4/2017 11:03 PM, zbigniew.bodek@caviumnetworks.com wrote:
>> From: Zbigniew Bodek <zbigniew.bodek@caviumnetworks.com>
>>
>> This patch introduces crypto poll mode driver
>> using ARMv8 cryptographic extensions.
>> CPU compatibility with this driver is detected in
>> run-time and virtual crypto device will not be
>> created if CPU doesn't provide:
>> AES, SHA1, SHA2 and NEON.
>>
>> This PMD is optimized to provide performance boost
>> for chained crypto operations processing,
>> such as encryption + HMAC generation,
>> decryption + HMAC validation. In particular,
>> cipher only or hash only operations are
>> not provided.
>>
>> The driver currently supports AES-128-CBC
>> in combination with: SHA256 HMAC and SHA1 HMAC
>> and relies on the external armv8_crypto library:
>> https://github.com/caviumnetworks/armv8_crypto
>>
>> This patch adds driver's code only and does
>> not include it in the build system.
>>
>> Signed-off-by: Zbigniew Bodek <zbigniew.bodek@caviumnetworks.com>
>> ---
>>  drivers/crypto/armv8/Makefile                  |  73 ++
>>  drivers/crypto/armv8/rte_armv8_pmd.c           | 926
>> +++++++++++++++++++++++++
>>  drivers/crypto/armv8/rte_armv8_pmd_ops.c       | 369 ++++++++++
>>  drivers/crypto/armv8/rte_armv8_pmd_private.h   | 211 ++++++
>>  drivers/crypto/armv8/rte_armv8_pmd_version.map |   3 +
>>  5 files changed, 1582 insertions(+)
>>  create mode 100644 drivers/crypto/armv8/Makefile
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd.c
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_ops.c
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_private.h
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_version.map
>>
>> diff --git a/drivers/crypto/armv8/Makefile
>> b/drivers/crypto/armv8/Makefile
>> new file mode 100644
>> index 0000000..dc5ea02
>> --- /dev/null
>> +++ b/drivers/crypto/armv8/Makefile
>> @@ -0,0 +1,73 @@
>> +#
>> +#   BSD LICENSE
>> +#
>> +#   Copyright (C) Cavium networks Ltd. 2017.
>> +#
>> +#   Redistribution and use in source and binary forms, with or without
>> +#   modification, are permitted provided that the following conditions
>> +#   are met:
>> +#
>> +#     * Redistributions of source code must retain the above copyright
>> +#       notice, this list of conditions and the following disclaimer.
>> +#     * Redistributions in binary form must reproduce the above
>> copyright
>> +#       notice, this list of conditions and the following disclaimer in
>> +#       the documentation and/or other materials provided with the
>> +#       distribution.
>> +#     * Neither the name of Cavium networks nor the names of its
>> +#       contributors may be used to endorse or promote products derived
>> +#       from this software without specific prior written permission.
>> +#
>> +#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
>> +#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
>> +#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
>> FOR
>> +#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
>> +#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
>> INCIDENTAL,
>> +#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>> +#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>> USE,
>> +#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
>> ANY
>> +#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
>> +#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
>> USE
>> +#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>> +#
>> +
>> +include $(RTE_SDK)/mk/rte.vars.mk
>> +
>> +ifneq ($(MAKECMDGOALS),clean)
>> +ifneq ($(MAKECMDGOALS),config)
>> +ifeq ($(ARMV8_CRYPTO_LIB_PATH),)
>> +$(error "Please define ARMV8_CRYPTO_LIB_PATH environment variable")
>> +endif
>> +endif
>> +endif
>> +
>> +# library name
>> +LIB = librte_pmd_armv8.a
>> +
>> +# build flags
>> +CFLAGS += -O3
>> +CFLAGS += $(WERROR_FLAGS)
>> +CFLAGS += -L$(RTE_SDK)/../openssl -I$(RTE_SDK)/../openssl/include
>> +
>> +# library version
>> +LIBABIVER := 1
>> +
>> +# versioning export map
>> +EXPORT_MAP := rte_armv8_pmd_version.map
>> +
>> +# external library dependencies
>> +CFLAGS += -I$(ARMV8_CRYPTO_LIB_PATH)
>> +CFLAGS += -I$(ARMV8_CRYPTO_LIB_PATH)/asm/include
>> +LDLIBS += -L$(ARMV8_CRYPTO_LIB_PATH) -larmv8_crypto
>> +
>> +# library source files
>> +SRCS-$(CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO) += rte_armv8_pmd.c
>> +SRCS-$(CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO) += rte_armv8_pmd_ops.c
>> +
>> +# library dependencies
>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO) += lib/librte_eal
>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO) += lib/librte_mbuf
>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO) += lib/librte_mempool
>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO) += lib/librte_ring
>> +DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO) += lib/librte_cryptodev
>> +
>> +include $(RTE_SDK)/mk/rte.lib.mk
>> diff --git a/drivers/crypto/armv8/rte_armv8_pmd.c
>> b/drivers/crypto/armv8/rte_armv8_pmd.c
>> new file mode 100644
>> index 0000000..39433bb
>> --- /dev/null
>> +++ b/drivers/crypto/armv8/rte_armv8_pmd.c
>> @@ -0,0 +1,926 @@
>> +/*
>> + *   BSD LICENSE
>> + *
>> + *   Copyright (C) Cavium networks Ltd. 2017.
>> + *
>> + *   Redistribution and use in source and binary forms, with or without
>> + *   modification, are permitted provided that the following conditions
>> + *   are met:
>> + *
>> + *     * Redistributions of source code must retain the above copyright
>> + *       notice, this list of conditions and the following disclaimer.
>> + *     * Redistributions in binary form must reproduce the above
>> copyright
>> + *       notice, this list of conditions and the following disclaimer in
>> + *       the documentation and/or other materials provided with the
>> + *       distribution.
>> + *     * Neither the name of Cavium networks nor the names of its
>> + *       contributors may be used to endorse or promote products derived
>> + *       from this software without specific prior written permission.
>> + *
>> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
>> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
>> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
>> FITNESS FOR
>> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
>> COPYRIGHT
>> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
>> INCIDENTAL,
>> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>> USE,
>> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>> ON ANY
>> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
>> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
>> THE USE
>> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
>> DAMAGE.
>> + */
>> +
>> +#include <stdbool.h>
>> +
>> +#include <rte_common.h>
>> +#include <rte_hexdump.h>
>> +#include <rte_cryptodev.h>
>> +#include <rte_cryptodev_pmd.h>
>> +#include <rte_vdev.h>
>> +#include <rte_malloc.h>
>> +#include <rte_cpuflags.h>
>> +
>> +#include "armv8_crypto_defs.h"
>> +
>> +#include "rte_armv8_pmd_private.h"
>> +
>> +static int cryptodev_armv8_crypto_uninit(const char *name);
>> +
>> +/**
>> + * Pointers to the supported combined mode crypto functions are stored
>> + * in the static tables. Each combined (chained) cryptographic operation
>> + * can be decribed by a set of numbers:
>
> replace "decribed" with "described"

Thanks. Done.
>
>> + * - order:    order of operations (cipher, auth) or (auth, cipher)
>> + * - direction:    encryption or decryption
>> + * - calg:    cipher algorithm such as AES_CBC, AES_CTR, etc.
>> + * - aalg:    authentication algorithm such as SHA1, SHA256, etc.
>> + * - keyl:    cipher key length, for example 128, 192, 256 bits
>> + *
>> + * In order to quickly acquire each function pointer based on those
>> numbers,
>> + * a hierarchy of arrays is maintained. The final level, 3D array is
>> indexed
>> + * by the combined mode function parameters only (cipher algorithm,
>> + * authentication algorithm and key length).
>> + *
>> + * This gives 3 memory accesses to obtain a function pointer instead of
>> + * traversing the array manually and comparing function parameters on
>> each loop.
>> + *
>> + *                   +--+CRYPTO_FUNC
>> + *            +--+ENC|
>> + *      +--+CA|
>> + *      |     +--+DEC
>> + * ORDER|
>> + *      |     +--+ENC
>> + *      +--+AC|
>> + *            +--+DEC
>> + *
>> + */
>> +
>> +/**
>> + * 3D array type for ARM Combined Mode crypto functions pointers.
>> + * CRYPTO_CIPHER_MAX:            max cipher ID number
>> + * CRYPTO_AUTH_MAX:            max auth ID number
>> + * CRYPTO_CIPHER_KEYLEN_MAX:        max key length ID number
>> + */
>> +typedef const crypto_func_t
>> +crypto_func_tbl_t[CRYPTO_CIPHER_MAX][CRYPTO_AUTH_MAX][CRYPTO_CIPHER_KEYLEN_MAX];
>>
>> +
>> +/* Evaluate to key length definition */
>> +#define KEYL(keyl)        (ARMV8_CRYPTO_CIPHER_KEYLEN_ ## keyl)
>> +
>> +/* Local aliases for supported ciphers */
>> +#define CIPH_AES_CBC        RTE_CRYPTO_CIPHER_AES_CBC
>> +/* Local aliases for supported hashes */
>> +#define AUTH_SHA1_HMAC        RTE_CRYPTO_AUTH_SHA1_HMAC
>> +#define AUTH_SHA256        RTE_CRYPTO_AUTH_SHA256
>
> SHA256 you are defining both AUTH and HMAC, however for SHA1 only HMAC.
> In your implementation, you seems to be only supporting HMAC.

Yes. This is removed now. The MAC implementation will not be included in 
this patchset.

>
>> +#define AUTH_SHA256_HMAC    RTE_CRYPTO_AUTH_SHA256_HMAC
>> +
>> +/**
>> + * Arrays containing pointers to particular cryptographic,
>> + * combined mode functions.
>> + * crypto_op_ca_encrypt:    cipher (encrypt), authenticate
>> + * crypto_op_ca_decrypt:    cipher (decrypt), authenticate
>> + * crypto_op_ac_encrypt:    authenticate, cipher (encrypt)
>> + * crypto_op_ac_decrypt:    authenticate, cipher (decrypt)
>> + */
>> +static const crypto_func_tbl_t
>> +crypto_op_ca_encrypt = {
>> +    /* [cipher alg][auth alg][key length] = crypto_function, */
>> +    [CIPH_AES_CBC][AUTH_SHA1_HMAC][KEYL(128)] = aes128cbc_sha1_hmac,
>> +    [CIPH_AES_CBC][AUTH_SHA256_HMAC][KEYL(128)] = aes128cbc_sha256_hmac,
>> +};
>> +
> do you plan to support aes192 and aes256 as well?

Yes, in the future. Based on our resource availability. This patchset 
will contain only AES128CBC

>
>> +static const crypto_func_tbl_t
>> +crypto_op_ca_decrypt = {
>> +    NULL
>> +};
>> +
>> +static const crypto_func_tbl_t
>> +crypto_op_ac_encrypt = {
>> +    NULL
>> +};
>> +
>> +static const crypto_func_tbl_t
>> +crypto_op_ac_decrypt = {
>> +    /* [cipher alg][auth alg][key length] = crypto_function, */
>> +    [CIPH_AES_CBC][AUTH_SHA1_HMAC][KEYL(128)] = sha1_hmac_aes128cbc_dec,
>> +    [CIPH_AES_CBC][AUTH_SHA256_HMAC][KEYL(128)] =
>> sha256_hmac_aes128cbc_dec,
>> +};
>> +
>> +/**
>> + * Arrays containing pointers to particular cryptographic function sets,
>> + * covering given cipher operation directions (encrypt, decrypt)
>> + * for each order of cipher and authentication pairs.
>> + */
>> +static const crypto_func_tbl_t *
>> +crypto_cipher_auth[] = {
>> +    &crypto_op_ca_encrypt,
>> +    &crypto_op_ca_decrypt,
>> +    NULL
>> +};
>> +
>> +static const crypto_func_tbl_t *
>> +crypto_auth_cipher[] = {
>> +    &crypto_op_ac_encrypt,
>> +    &crypto_op_ac_decrypt,
>> +    NULL
>> +};
>> +
>> +/**
>> + * Top level array containing pointers to particular cryptographic
>> + * function sets, covering given order of chained operations.
>> + * crypto_cipher_auth:    cipher first, authenticate after
>> + * crypto_auth_cipher:    authenticate first, cipher after
>> + */
>> +static const crypto_func_tbl_t **
>> +crypto_chain_order[] = {
>> +    crypto_cipher_auth,
>> +    crypto_auth_cipher,
>> +    NULL
>> +};
>> +
>> +/**
>> + * Extract particular combined mode crypto function from the 3D array.
>> + */
>> +#define CRYPTO_GET_ALGO(order, cop, calg, aalg, keyl)            \
>> +({                                    \
>> +    crypto_func_tbl_t *func_tbl =                    \
>> +                (crypto_chain_order[(order)])[(cop)];    \
>> +                                    \
>> +    ((*func_tbl)[(calg)][(aalg)][KEYL(keyl)]);        \
>> +})
>> +
>> +/*----------------------------------------------------------------------------*/
>>
>> +
>> +/**
>> + * 2D array type for ARM key schedule functions pointers.
>> + * CRYPTO_CIPHER_MAX:            max cipher ID number
>> + * CRYPTO_CIPHER_KEYLEN_MAX:        max key length ID number
>> + */
>> +typedef const crypto_key_sched_t
>> +crypto_key_sched_tbl_t[CRYPTO_CIPHER_MAX][CRYPTO_CIPHER_KEYLEN_MAX];
>> +
>> +static const crypto_key_sched_tbl_t
>> +crypto_key_sched_encrypt = {
>> +    /* [cipher alg][key length] = key_expand_func, */
>> +    [CIPH_AES_CBC][KEYL(128)] = aes128_key_sched_enc,
>> +};
>> +
>> +static const crypto_key_sched_tbl_t
>> +crypto_key_sched_decrypt = {
>> +    /* [cipher alg][key length] = key_expand_func, */
>> +    [CIPH_AES_CBC][KEYL(128)] = aes128_key_sched_dec,
>> +};
>> +
>> +/**
>> + * Top level array containing pointers to particular key generation
>> + * function sets, covering given operation direction.
>> + * crypto_key_sched_encrypt:    keys for encryption
>> + * crypto_key_sched_decrypt:    keys for decryption
>> + */
>> +static const crypto_key_sched_tbl_t *
>> +crypto_key_sched_dir[] = {
>> +    &crypto_key_sched_encrypt,
>> +    &crypto_key_sched_decrypt,
>> +    NULL
>> +};
>> +
>> +/**
>> + * Extract particular combined mode crypto function from the 3D array.
>> + */
>> +#define CRYPTO_GET_KEY_SCHED(cop, calg, keyl)                \
>> +({                                    \
>> +    crypto_key_sched_tbl_t *ks_tbl = crypto_key_sched_dir[(cop)];    \
>> +                                    \
>> +    ((*ks_tbl)[(calg)][KEYL(keyl)]);                \
>> +})
>> +
>> +/*----------------------------------------------------------------------------*/
>>
>> +
>> +/**
>> + * Global static parameter used to create a unique name for each
>> + * ARMV8 crypto device.
>> + */
>> +static unsigned int unique_name_id;
>> +
>> +static inline int
>> +create_unique_device_name(char *name, size_t size)
>> +{
>> +    int ret;
>> +
>> +    if (name == NULL)
>> +        return -EINVAL;
>> +
>> +    ret = snprintf(name, size, "%s_%u",
>> RTE_STR(CRYPTODEV_NAME_ARMV8_PMD),
>> +            unique_name_id++);
>> +    if (ret < 0)
>> +        return ret;
>> +    return 0;
>> +}
>> +
>> +/*
>> +
>> *------------------------------------------------------------------------------
>>
>> + * Session Prepare
>> +
>> *------------------------------------------------------------------------------
>>
>> + */
>> +
>> +/** Get xform chain order */
>> +static enum armv8_crypto_chain_order
>> +armv8_crypto_get_chain_order(const struct rte_crypto_sym_xform *xform)
>> +{
>> +
>> +    /*
>> +     * This driver currently covers only chained operations.
>> +     * Ignore only cipher or only authentication operations
>> +     * or chains longer than 2 xform structures.
>> +     */
>> +    if (xform->next == NULL || xform->next->next != NULL)
>> +        return ARMV8_CRYPTO_CHAIN_NOT_SUPPORTED;
>> +
>> +    if (xform->type == RTE_CRYPTO_SYM_XFORM_AUTH) {
>> +        if (xform->next->type == RTE_CRYPTO_SYM_XFORM_CIPHER)
>> +            return ARMV8_CRYPTO_CHAIN_AUTH_CIPHER;
>> +    }
>> +
>> +    if (xform->type == RTE_CRYPTO_SYM_XFORM_CIPHER) {
>> +        if (xform->next->type == RTE_CRYPTO_SYM_XFORM_AUTH)
>> +            return ARMV8_CRYPTO_CHAIN_CIPHER_AUTH;
>> +    }
>> +
>> +    return ARMV8_CRYPTO_CHAIN_NOT_SUPPORTED;
>> +}
>> +
>> +static inline void
>> +auth_hmac_pad_prepare(struct armv8_crypto_session *sess,
>> +                const struct rte_crypto_sym_xform *xform)
>> +{
>> +    size_t i;
>> +
>> +    /* Generate i_key_pad and o_key_pad */
>> +    memset(sess->auth.hmac.i_key_pad, 0,
>> sizeof(sess->auth.hmac.i_key_pad));
>> +    rte_memcpy(sess->auth.hmac.i_key_pad, sess->auth.hmac.key,
>> +                            xform->auth.key.length);
>> +    memset(sess->auth.hmac.o_key_pad, 0,
>> sizeof(sess->auth.hmac.o_key_pad));
>> +    rte_memcpy(sess->auth.hmac.o_key_pad, sess->auth.hmac.key,
>> +                            xform->auth.key.length);
>> +    /*
>> +     * XOR key with IPAD/OPAD values to obtain i_key_pad
>> +     * and o_key_pad.
>> +     * Byte-by-byte operation may seem to be the less efficient
>> +     * here but in fact it's the opposite.
>> +     * The result ASM code is likely operate on NEON registers
>> +     * (load auth key to Qx, load IPAD/OPAD to multiple
>> +     * elements of Qy, eor 128 bits at once).
>> +     */
>> +    for (i = 0; i < SHA_BLOCK_MAX; i++) {
>> +        sess->auth.hmac.i_key_pad[i] ^= HMAC_IPAD_VALUE;
>> +        sess->auth.hmac.o_key_pad[i] ^= HMAC_OPAD_VALUE;
>> +    }
>> +}
>> +
>> +static inline int
>> +auth_set_prerequisites(struct armv8_crypto_session *sess,
>> +            const struct rte_crypto_sym_xform *xform)
>> +{
>> +    uint8_t partial[64] = { 0 };
>> +    int error;
>> +
>> +    switch (xform->auth.algo) {
>> +    case RTE_CRYPTO_AUTH_SHA1_HMAC:
>> +        /*
>> +         * Generate authentication key, i_key_pad and o_key_pad.
>> +         */
>> +        /* Zero memory under key */
>> +        memset(sess->auth.hmac.key, 0, SHA1_AUTH_KEY_LENGTH);
>> +
>> +        if (xform->auth.key.length > SHA1_AUTH_KEY_LENGTH) {
>> +            /*
>> +             * In case the key is longer than 160 bits
>> +             * the algorithm will use SHA1(key) instead.
>> +             */
>> +            error = sha1_block(NULL, xform->auth.key.data,
>> +                sess->auth.hmac.key, xform->auth.key.length);
>> +            if (error != 0)
>> +                return -1;
>> +        } else {
>> +            /*
>> +             * Now copy the given authentication key to the session
>> +             * key assuming that the session key is zeroed there is
>> +             * no need for additional zero padding if the key is
>> +             * shorter than SHA1_AUTH_KEY_LENGTH.
>> +             */
>> +            rte_memcpy(sess->auth.hmac.key, xform->auth.key.data,
>> +                            xform->auth.key.length);
>> +        }
>> +
>> +        /* Prepare HMAC padding: key|pattern */
>> +        auth_hmac_pad_prepare(sess, xform);
>> +        /*
>> +         * Calculate partial hash values for i_key_pad and o_key_pad.
>> +         * Will be used as initialization state for final HMAC.
>> +         */
>> +        error = sha1_block_partial(NULL, sess->auth.hmac.i_key_pad,
>> +            partial, SHA1_BLOCK_SIZE);
>> +        if (error != 0)
>> +            return -1;
>> +        memcpy(sess->auth.hmac.i_key_pad, partial, SHA1_BLOCK_SIZE);
>> +
>> +        error = sha1_block_partial(NULL, sess->auth.hmac.o_key_pad,
>> +            partial, SHA1_BLOCK_SIZE);
>> +        if (error != 0)
>> +            return -1;
>> +        memcpy(sess->auth.hmac.o_key_pad, partial, SHA1_BLOCK_SIZE);
>> +
>> +        break;
>> +    case RTE_CRYPTO_AUTH_SHA256_HMAC:
>> +        /*
>> +         * Generate authentication key, i_key_pad and o_key_pad.
>> +         */
>> +        /* Zero memory under key */
>> +        memset(sess->auth.hmac.key, 0, SHA256_AUTH_KEY_LENGTH);
>> +
>> +        if (xform->auth.key.length > SHA256_AUTH_KEY_LENGTH) {
>> +            /*
>> +             * In case the key is longer than 256 bits
>> +             * the algorithm will use SHA256(key) instead.
>> +             */
>> +            error = sha256_block(NULL, xform->auth.key.data,
>> +                sess->auth.hmac.key, xform->auth.key.length);
>> +            if (error != 0)
>> +                return -1;
>> +        } else {
>> +            /*
>> +             * Now copy the given authentication key to the session
>> +             * key assuming that the session key is zeroed there is
>> +             * no need for additional zero padding if the key is
>> +             * shorter than SHA256_AUTH_KEY_LENGTH.
>> +             */
>> +            rte_memcpy(sess->auth.hmac.key, xform->auth.key.data,
>> +                            xform->auth.key.length);
>> +        }
>> +
>> +        /* Prepare HMAC padding: key|pattern */
>> +        auth_hmac_pad_prepare(sess, xform);
>> +        /*
>> +         * Calculate partial hash values for i_key_pad and o_key_pad.
>> +         * Will be used as initialization state for final HMAC.
>> +         */
>> +        error = sha256_block_partial(NULL, sess->auth.hmac.i_key_pad,
>> +            partial, SHA256_BLOCK_SIZE);
>> +        if (error != 0)
>> +            return -1;
>> +        memcpy(sess->auth.hmac.i_key_pad, partial, SHA256_BLOCK_SIZE);
>> +
>> +        error = sha256_block_partial(NULL, sess->auth.hmac.o_key_pad,
>> +            partial, SHA256_BLOCK_SIZE);
>> +        if (error != 0)
>> +            return -1;
>> +        memcpy(sess->auth.hmac.o_key_pad, partial, SHA256_BLOCK_SIZE);
>> +
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static inline int
>> +cipher_set_prerequisites(struct armv8_crypto_session *sess,
>> +            const struct rte_crypto_sym_xform *xform)
>> +{
>> +    crypto_key_sched_t cipher_key_sched;
>> +
>> +    cipher_key_sched = sess->cipher.key_sched;
>> +    if (likely(cipher_key_sched != NULL)) {
>> +        /* Set up cipher session key */
>> +        cipher_key_sched(sess->cipher.key.data, xform->cipher.key.data);
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int
>> +armv8_crypto_set_session_chained_parameters(struct
>> armv8_crypto_session *sess,
>> +        const struct rte_crypto_sym_xform *cipher_xform,
>> +        const struct rte_crypto_sym_xform *auth_xform)
>> +{
>> +    enum armv8_crypto_chain_order order;
>> +    enum armv8_crypto_cipher_operation cop;
>> +    enum rte_crypto_cipher_algorithm calg;
>> +    enum rte_crypto_auth_algorithm aalg;
>> +
>> +    /* Validate and prepare scratch order of combined operations */
>> +    switch (sess->chain_order) {
>> +    case ARMV8_CRYPTO_CHAIN_CIPHER_AUTH:
>> +    case ARMV8_CRYPTO_CHAIN_AUTH_CIPHER:
>> +        order = sess->chain_order;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +    /* Select cipher direction */
>> +    sess->cipher.direction = cipher_xform->cipher.op;
>> +    /* Select cipher key */
>> +    sess->cipher.key.length = cipher_xform->cipher.key.length;
>> +    /* Set cipher direction */
>> +    cop = sess->cipher.direction;
>> +    /* Set cipher algorithm */
>> +    calg = cipher_xform->cipher.algo;
>> +
>> +    /* Select cipher algo */
>> +    switch (calg) {
>> +    /* Cover supported cipher algorithms */
>> +    case RTE_CRYPTO_CIPHER_AES_CBC:
>> +        sess->cipher.algo = calg;
>> +        /* IV len is always 16 bytes (block size) for AES CBC */
>> +        sess->cipher.iv_len = 16;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +    /* Select auth generate/verify */
>> +    sess->auth.operation = auth_xform->auth.op;
>> +
>> +    /* Select auth algo */
>> +    switch (auth_xform->auth.algo) {
>> +    /* Cover supported hash algorithms */
>> +    case RTE_CRYPTO_AUTH_SHA256:
>> +        aalg = auth_xform->auth.algo;
>> +        sess->auth.mode = ARMV8_CRYPTO_AUTH_AS_AUTH;
>> +        break;
>
> as previously stated, are you supporting AUTH types?

MAC is not supported in this patchset. Removed.
>
>
>> +    case RTE_CRYPTO_AUTH_SHA1_HMAC:
>> +    case RTE_CRYPTO_AUTH_SHA256_HMAC: /* Fall through */
>> +        aalg = auth_xform->auth.algo;
>> +        sess->auth.mode = ARMV8_CRYPTO_AUTH_AS_HMAC;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Verify supported key lengths and extract proper algorithm */
>> +    switch (cipher_xform->cipher.key.length << 3) {
>> +    case 128:
>> +        sess->crypto_func =
>> +                CRYPTO_GET_ALGO(order, cop, calg, aalg, 128);
>> +        sess->cipher.key_sched =
>> +                CRYPTO_GET_KEY_SCHED(cop, calg, 128);
>> +        break;
>> +    case 192:
>
> aes192 and aes256?

Set as default - unsupported in the new patchset.

>
>> +        sess->crypto_func =
>> +                CRYPTO_GET_ALGO(order, cop, calg, aalg, 192);
>> +        sess->cipher.key_sched =
>> +                CRYPTO_GET_KEY_SCHED(cop, calg, 192);
>> +        break;
>> +    case 256:
>> +        sess->crypto_func =
>> +                CRYPTO_GET_ALGO(order, cop, calg, aalg, 256);
>> +        sess->cipher.key_sched =
>> +                CRYPTO_GET_KEY_SCHED(cop, calg, 256);
>> +        break;
>> +    default:
>> +        sess->crypto_func = NULL;
>> +        sess->cipher.key_sched = NULL;
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (unlikely(sess->crypto_func == NULL)) {
>> +        /*
>> +         * If we got here that means that there must be a bug
>> +         * in the algorithms selection above. Nevertheless keep
>> +         * it here to catch bug immediately and avoid NULL pointer
>> +         * dereference in OPs processing.
>> +         */
>> +        ARMV8_CRYPTO_LOG_ERR(
>> +            "No appropriate crypto function for given parameters");
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Set up cipher session prerequisites */
>> +    if (cipher_set_prerequisites(sess, cipher_xform) != 0)
>> +        return -EINVAL;
>> +
>> +    /* Set up authentication session prerequisites */
>> +    if (auth_set_prerequisites(sess, auth_xform) != 0)
>> +        return -EINVAL;
>> +
>> +    return 0;
>> +}
>> +
>> +/** Parse crypto xform chain and set private session parameters */
>> +int
>> +armv8_crypto_set_session_parameters(struct armv8_crypto_session *sess,
>> +        const struct rte_crypto_sym_xform *xform)
>> +{
>> +    const struct rte_crypto_sym_xform *cipher_xform = NULL;
>> +    const struct rte_crypto_sym_xform *auth_xform = NULL;
>> +    bool is_chained_op;
>> +    int ret;
>> +
>> +    /* Filter out spurious/broken requests */
>> +    if (xform == NULL)
>> +        return -EINVAL;
>> +
>> +    sess->chain_order = armv8_crypto_get_chain_order(xform);
>> +    switch (sess->chain_order) {
>> +    case ARMV8_CRYPTO_CHAIN_CIPHER_AUTH:
>> +        cipher_xform = xform;
>> +        auth_xform = xform->next;
>> +        is_chained_op = true;
>> +        break;
>> +    case ARMV8_CRYPTO_CHAIN_AUTH_CIPHER:
>> +        auth_xform = xform;
>> +        cipher_xform = xform->next;
>> +        is_chained_op = true;
>> +        break;
>> +    default:
>> +        is_chained_op = false;
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (is_chained_op) {
>> +        ret = armv8_crypto_set_session_chained_parameters(sess,
>> +                        cipher_xform, auth_xform);
>> +        if (unlikely(ret != 0)) {
>> +            ARMV8_CRYPTO_LOG_ERR(
>> +            "Invalid/unsupported chained (cipher/auth) parameters");
>> +            return -EINVAL;
>> +        }
>> +    } else {
>> +        ARMV8_CRYPTO_LOG_ERR("Invalid/unsupported operation");
>> +        return -EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/** Provide session for operation */
>> +static struct armv8_crypto_session *
>> +get_session(struct armv8_crypto_qp *qp, struct rte_crypto_op *op)
>> +{
>> +    struct armv8_crypto_session *sess = NULL;
>> +
>> +    if (op->sym->sess_type == RTE_CRYPTO_SYM_OP_WITH_SESSION) {
>> +        /* get existing session */
>> +        if (likely(op->sym->session != NULL &&
>> +                op->sym->session->dev_type ==
>> +                RTE_CRYPTODEV_ARMV8_PMD)) {
>> +            sess = (struct armv8_crypto_session *)
>> +                op->sym->session->_private;
>> +        }
>> +    } else {
>> +        /* provide internal session */
>> +        void *_sess = NULL;
>> +
>> +        if (!rte_mempool_get(qp->sess_mp, (void **)&_sess)) {
>> +            sess = (struct armv8_crypto_session *)
>> +                ((struct rte_cryptodev_sym_session *)_sess)
>> +                ->_private;
>> +
>> +            if (unlikely(armv8_crypto_set_session_parameters(
>> +                    sess, op->sym->xform) != 0)) {
>> +                rte_mempool_put(qp->sess_mp, _sess);
>> +                sess = NULL;
>> +            } else
>> +                op->sym->session = _sess;
>> +        }
>> +    }
>> +
>> +    if (sess == NULL)
>> +        op->status = RTE_CRYPTO_OP_STATUS_INVALID_SESSION;
>> +
>> +    return sess;
>> +}
>> +
>> +/*
>> +
>> *------------------------------------------------------------------------------
>>
>> + * Process Operations
>> +
>> *------------------------------------------------------------------------------
>>
>> + */
>> +
>> +/*----------------------------------------------------------------------------*/
>>
>> +
>> +/** Process cipher operation */
>> +static void
>> +process_armv8_chained_op
>> +        (struct rte_crypto_op *op, struct armv8_crypto_session *sess,
>> +        struct rte_mbuf *mbuf_src, struct rte_mbuf *mbuf_dst)
>> +{
>> +    crypto_func_t crypto_func;
>> +    crypto_arg_t arg;
>> +    struct rte_mbuf *m_asrc, *m_adst;
>> +    uint8_t *csrc, *cdst;
>> +    uint8_t *adst, *asrc;
>> +    uint64_t clen, alen __rte_unused;
>> +    int error;
>> +
>> +    clen = op->sym->cipher.data.length;
>> +    alen = op->sym->auth.data.length;
>> +
>> +    csrc = rte_pktmbuf_mtod_offset(mbuf_src, uint8_t *,
>> +            op->sym->cipher.data.offset);
>> +    cdst = rte_pktmbuf_mtod_offset(mbuf_dst, uint8_t *,
>> +            op->sym->cipher.data.offset);
>> +
>> +    switch (sess->chain_order) {
>> +    case ARMV8_CRYPTO_CHAIN_CIPHER_AUTH:
>> +        m_asrc = m_adst = mbuf_dst;
>> +        break;
>> +    case ARMV8_CRYPTO_CHAIN_AUTH_CIPHER:
>> +        m_asrc = mbuf_src;
>> +        m_adst = mbuf_dst;
>> +        break;
>> +    default:
>> +        op->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
>> +        return;
>> +    }
>> +    asrc = rte_pktmbuf_mtod_offset(m_asrc, uint8_t *,
>> +                op->sym->auth.data.offset);
>> +
>> +    switch (sess->auth.mode) {
>> +    case ARMV8_CRYPTO_AUTH_AS_AUTH:
>> +        /* Nothing to do here, just verify correct option */
>> +        break;
>> +    case ARMV8_CRYPTO_AUTH_AS_HMAC:
>> +        arg.digest.hmac.key = sess->auth.hmac.key;
>> +        arg.digest.hmac.i_key_pad = sess->auth.hmac.i_key_pad;
>> +        arg.digest.hmac.o_key_pad = sess->auth.hmac.o_key_pad;
>> +        break;
>> +    default:
>> +        op->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
>> +        return;
>> +    }
>> +
>> +    if (sess->auth.operation == RTE_CRYPTO_AUTH_OP_GENERATE) {
>> +        adst = op->sym->auth.digest.data;
>> +        if (adst == NULL) {
>> +            adst = rte_pktmbuf_mtod_offset(m_adst,
>> +                    uint8_t *,
>> +                    op->sym->auth.data.offset +
>> +                    op->sym->auth.data.length);
>> +        }
>> +    } else {
>> +        adst = (uint8_t *)rte_pktmbuf_append(m_asrc,
>> +                op->sym->auth.digest.length);
>> +    }
>> +
>> +    if (unlikely(op->sym->cipher.iv.length != sess->cipher.iv_len)) {
>> +        op->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
>> +        return;
>> +    }
>> +
>> +    arg.cipher.iv = op->sym->cipher.iv.data;
>> +    arg.cipher.key = sess->cipher.key.data;
>> +    /* Acquire combined mode function */
>> +    crypto_func = sess->crypto_func;
>> +    ARMV8_CRYPTO_ASSERT(crypto_func != NULL);
>> +    error = crypto_func(csrc, cdst, clen, asrc, adst, alen, &arg);
>> +    if (error != 0) {
>> +        op->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
>> +        return;
>> +    }
>> +
>> +    op->status = RTE_CRYPTO_OP_STATUS_SUCCESS;
>> +    if (sess->auth.operation == RTE_CRYPTO_AUTH_OP_VERIFY) {
>> +        if (memcmp(adst, op->sym->auth.digest.data,
>> +                op->sym->auth.digest.length) != 0) {
>> +            op->status = RTE_CRYPTO_OP_STATUS_AUTH_FAILED;
>> +        }
>> +        /* Trim area used for digest from mbuf. */
>> +        rte_pktmbuf_trim(m_asrc,
>> +                op->sym->auth.digest.length);
>> +    }
>> +}
>> +
>> +/** Process crypto operation for mbuf */
>> +static int
>> +process_op(const struct armv8_crypto_qp *qp, struct rte_crypto_op *op,
>> +        struct armv8_crypto_session *sess)
>> +{
>> +    struct rte_mbuf *msrc, *mdst;
>> +    int retval;
>> +
>> +    msrc = op->sym->m_src;
>> +    mdst = op->sym->m_dst ? op->sym->m_dst : op->sym->m_src;
>> +
>> +    op->status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED;
>> +
>> +    switch (sess->chain_order) {
>> +    case ARMV8_CRYPTO_CHAIN_CIPHER_AUTH:
>> +    case ARMV8_CRYPTO_CHAIN_AUTH_CIPHER: /* Fall through */
>> +        process_armv8_chained_op(op, sess, msrc, mdst);
>> +        break;
>> +    default:
>> +        op->status = RTE_CRYPTO_OP_STATUS_ERROR;
>> +        break;
>> +    }
>> +
>> +    /* Free session if a session-less crypto op */
>> +    if (op->sym->sess_type == RTE_CRYPTO_SYM_OP_SESSIONLESS) {
>> +        memset(sess, 0, sizeof(struct armv8_crypto_session));
>> +        rte_mempool_put(qp->sess_mp, op->sym->session);
>> +        op->sym->session = NULL;
>> +    }
>> +
>> +    if (op->status == RTE_CRYPTO_OP_STATUS_NOT_PROCESSED)
>> +        op->status = RTE_CRYPTO_OP_STATUS_SUCCESS;
>> +
>> +    if (op->status != RTE_CRYPTO_OP_STATUS_ERROR)
>> +        retval = rte_ring_enqueue(qp->processed_ops, (void *)op);
>> +    else
>> +        retval = -1;
>> +
>> +    return retval;
>> +}
>> +
>> +/*
>> +
>> *------------------------------------------------------------------------------
>>
>> + * PMD Framework
>> +
>> *------------------------------------------------------------------------------
>>
>> + */
>> +
>> +/** Enqueue burst */
>> +static uint16_t
>> +armv8_crypto_pmd_enqueue_burst(void *queue_pair, struct rte_crypto_op
>> **ops,
>> +        uint16_t nb_ops)
>> +{
>> +    struct armv8_crypto_session *sess;
>> +    struct armv8_crypto_qp *qp = queue_pair;
>> +    int i, retval;
>> +
>> +    for (i = 0; i < nb_ops; i++) {
>> +        sess = get_session(qp, ops[i]);
>> +        if (unlikely(sess == NULL))
>> +            goto enqueue_err;
>> +
>> +        retval = process_op(qp, ops[i], sess);
>> +        if (unlikely(retval < 0))
>> +            goto enqueue_err;
>> +    }
>> +
>> +    qp->stats.enqueued_count += i;
>> +    return i;
>> +
>> +enqueue_err:
>> +    if (ops[i] != NULL)
>> +        ops[i]->status = RTE_CRYPTO_OP_STATUS_INVALID_ARGS;
>> +
>> +    qp->stats.enqueue_err_count++;
>> +    return i;
>> +}
>> +
>> +/** Dequeue burst */
>> +static uint16_t
>> +armv8_crypto_pmd_dequeue_burst(void *queue_pair, struct rte_crypto_op
>> **ops,
>> +        uint16_t nb_ops)
>> +{
>> +    struct armv8_crypto_qp *qp = queue_pair;
>> +
>> +    unsigned int nb_dequeued = 0;
>> +
>> +    nb_dequeued = rte_ring_dequeue_burst(qp->processed_ops,
>> +            (void **)ops, nb_ops);
>> +    qp->stats.dequeued_count += nb_dequeued;
>> +
>> +    return nb_dequeued;
>> +}
>> +
>> +/** Create ARMv8 crypto device */
>> +static int
>> +cryptodev_armv8_crypto_create(const char *name,
>> +        struct rte_crypto_vdev_init_params *init_params)
>> +{
>> +    struct rte_cryptodev *dev;
>> +    char crypto_dev_name[RTE_CRYPTODEV_NAME_MAX_LEN];
>> +    struct armv8_crypto_private *internals;
>> +
>> +    /* Check CPU for support for AES instruction set */
>> +    if (!rte_cpu_get_flag_enabled(RTE_CPUFLAG_AES)) {
>> +        ARMV8_CRYPTO_LOG_ERR(
>> +            "AES instructions not supported by CPU");
>> +        return -EFAULT;
>> +    }
>> +
>> +    /* Check CPU for support for SHA instruction set */
>> +    if (!rte_cpu_get_flag_enabled(RTE_CPUFLAG_SHA1) ||
>> +        !rte_cpu_get_flag_enabled(RTE_CPUFLAG_SHA2)) {
>> +        ARMV8_CRYPTO_LOG_ERR(
>> +            "SHA1/SHA2 instructions not supported by CPU");
>> +        return -EFAULT;
>> +    }
>> +
>> +    /* Check CPU for support for Advance SIMD instruction set */
>> +    if (!rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON)) {
>> +        ARMV8_CRYPTO_LOG_ERR(
>> +            "Advanced SIMD instructions not supported by CPU");
>> +        return -EFAULT;
>> +    }
>> +
>> +    /* create a unique device name */
>> +    if (create_unique_device_name(crypto_dev_name,
>> +            RTE_CRYPTODEV_NAME_MAX_LEN) != 0) {
>> +        ARMV8_CRYPTO_LOG_ERR("failed to create unique cryptodev name");
>> +        return -EINVAL;
>> +    }
>> +
>> +    dev = rte_cryptodev_pmd_virtual_dev_init(crypto_dev_name,
>> +                sizeof(struct armv8_crypto_private),
>> +                init_params->socket_id);
>> +    if (dev == NULL) {
>> +        ARMV8_CRYPTO_LOG_ERR("failed to create cryptodev vdev");
>> +        goto init_error;
>> +    }
>> +
>> +    dev->dev_type = RTE_CRYPTODEV_ARMV8_PMD;
>> +    dev->dev_ops = rte_armv8_crypto_pmd_ops;
>> +
>> +    /* register rx/tx burst functions for data path */
>> +    dev->dequeue_burst = armv8_crypto_pmd_dequeue_burst;
>> +    dev->enqueue_burst = armv8_crypto_pmd_enqueue_burst;
>> +
>> +    dev->feature_flags = RTE_CRYPTODEV_FF_SYMMETRIC_CRYPTO |
>> +            RTE_CRYPTODEV_FF_SYM_OPERATION_CHAINING;
>> +
>> +    /* Set vector instructions mode supported */
>> +    internals = dev->data->dev_private;
>> +
>> +    internals->max_nb_qpairs = init_params->max_nb_queue_pairs;
>> +    internals->max_nb_sessions = init_params->max_nb_sessions;
>> +
>> +    return 0;
>> +
>> +init_error:
>> +    ARMV8_CRYPTO_LOG_ERR(
>> +        "driver %s: cryptodev_armv8_crypto_create failed", name);
>> +
>> +    cryptodev_armv8_crypto_uninit(crypto_dev_name);
>> +    return -EFAULT;
>> +}
>> +
>> +/** Initialise ARMv8 crypto device */
>> +static int
>> +cryptodev_armv8_crypto_init(const char *name,
>> +        const char *input_args)
>> +{
>> +    struct rte_crypto_vdev_init_params init_params = {
>> +        RTE_CRYPTODEV_VDEV_DEFAULT_MAX_NB_QUEUE_PAIRS,
>> +        RTE_CRYPTODEV_VDEV_DEFAULT_MAX_NB_SESSIONS,
>> +        rte_socket_id()
>> +    };
>> +
>> +    rte_cryptodev_parse_vdev_init_params(&init_params, input_args);
>> +
>> +    RTE_LOG(INFO, PMD, "Initialising %s on NUMA node %d\n", name,
>> +            init_params.socket_id);
>> +    RTE_LOG(INFO, PMD, "  Max number of queue pairs = %d\n",
>> +            init_params.max_nb_queue_pairs);
>> +    RTE_LOG(INFO, PMD, "  Max number of sessions = %d\n",
>> +            init_params.max_nb_sessions);
>> +
>> +    return cryptodev_armv8_crypto_create(name, &init_params);
>> +}
>> +
>> +/** Uninitialise ARMv8 crypto device */
>> +static int
>> +cryptodev_armv8_crypto_uninit(const char *name)
>> +{
>> +    if (name == NULL)
>> +        return -EINVAL;
>> +
>> +    RTE_LOG(INFO, PMD,
>> +        "Closing ARMv8 crypto device %s on numa socket %u\n",
>> +        name, rte_socket_id());
>> +
>> +    return 0;
>> +}
>> +
>> +static struct rte_vdev_driver armv8_crypto_drv = {
>> +    .probe = cryptodev_armv8_crypto_init,
>> +    .remove = cryptodev_armv8_crypto_uninit
>> +};
>> +
>> +RTE_PMD_REGISTER_VDEV(CRYPTODEV_NAME_ARMV8_PMD, armv8_crypto_drv);
>> +RTE_PMD_REGISTER_ALIAS(CRYPTODEV_NAME_ARMV8_PMD, cryptodev_armv8_pmd);
>> +RTE_PMD_REGISTER_PARAM_STRING(CRYPTODEV_NAME_ARMV8_PMD,
>> +    "max_nb_queue_pairs=<int> "
>> +    "max_nb_sessions=<int> "
>> +    "socket_id=<int>");
>> diff --git a/drivers/crypto/armv8/rte_armv8_pmd_ops.c
>> b/drivers/crypto/armv8/rte_armv8_pmd_ops.c
>> new file mode 100644
>> index 0000000..2bf6475
>> --- /dev/null
>> +++ b/drivers/crypto/armv8/rte_armv8_pmd_ops.c
>> @@ -0,0 +1,369 @@
>> +/*
>> + *   BSD LICENSE
>> + *
>> + *   Copyright (C) Cavium networks Ltd. 2017.
>> + *
>> + *   Redistribution and use in source and binary forms, with or without
>> + *   modification, are permitted provided that the following conditions
>> + *   are met:
>> + *
>> + *     * Redistributions of source code must retain the above copyright
>> + *       notice, this list of conditions and the following disclaimer.
>> + *     * Redistributions in binary form must reproduce the above
>> copyright
>> + *       notice, this list of conditions and the following disclaimer in
>> + *       the documentation and/or other materials provided with the
>> + *       distribution.
>> + *     * Neither the name of Cavium networks nor the names of its
>> + *       contributors may be used to endorse or promote products derived
>> + *       from this software without specific prior written permission.
>> + *
>> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
>> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
>> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
>> FITNESS FOR
>> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
>> COPYRIGHT
>> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
>> INCIDENTAL,
>> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>> USE,
>> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>> ON ANY
>> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
>> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
>> THE USE
>> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
>> DAMAGE.
>> + */
>> +
>> +#include <string.h>
>> +
>> +#include <rte_common.h>
>> +#include <rte_malloc.h>
>> +#include <rte_cryptodev_pmd.h>
>> +
>> +#include "armv8_crypto_defs.h"
>> +
>> +#include "rte_armv8_pmd_private.h"
>> +
>> +static const struct rte_cryptodev_capabilities
>> +    armv8_crypto_pmd_capabilities[] = {
>> +    {    /* SHA1 HMAC */
>> +        .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
>> +            {.sym = {
>> +                .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
>> +                {.auth = {
>> +                    .algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
>> +                    .block_size = 64,
>> +                    .key_size = {
>> +                        .min = 16,
>> +                        .max = 128,
>> +                        .increment = 0
>> +                    },
>> +                    .digest_size = {
>> +                        .min = 20,
>> +                        .max = 20,
>> +                        .increment = 0
>> +                    },
>> +                    .aad_size = { 0 }
>> +                }, }
>> +            }, }
>> +    },
>> +    {    /* SHA256 HMAC */
>> +        .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
>> +            {.sym = {
>> +                .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
>> +                {.auth = {
>> +                    .algo = RTE_CRYPTO_AUTH_SHA256_HMAC,
>> +                    .block_size = 64,
>> +                    .key_size = {
>> +                        .min = 16,
>> +                        .max = 128,
>> +                        .increment = 0
>> +                    },
>> +                    .digest_size = {
>> +                        .min = 32,
>> +                        .max = 32,
>> +                        .increment = 0
>> +                    },
>> +                    .aad_size = { 0 }
>> +                }, }
>> +            }, }
>> +    },
>> +    {    /* AES CBC */
>> +        .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
>> +            {.sym = {
>> +                .xform_type = RTE_CRYPTO_SYM_XFORM_CIPHER,
>> +                {.cipher = {
>> +                    .algo = RTE_CRYPTO_CIPHER_AES_CBC,
>> +                    .block_size = 16,
>> +                    .key_size = {
>> +                        .min = 16,
>> +                        .max = 16,
>
> do you plan max = 32 ?
>
>> +                        .increment = 0
>> +                    },
>> +                    .iv_size = {
>> +                        .min = 16,
>> +                        .max = 16,
>> +                        .increment = 0
>> +                    }
>> +                }, }
>> +            }, }
>> +    },
>> +
>> +    RTE_CRYPTODEV_END_OF_CAPABILITIES_LIST()
>> +};
>> +
>> +
>> +/** Configure device */
>> +static int
>> +armv8_crypto_pmd_config(__rte_unused struct rte_cryptodev *dev)
>> +{
>> +    return 0;
>> +}
>> +
>> +/** Start device */
>> +static int
>> +armv8_crypto_pmd_start(__rte_unused struct rte_cryptodev *dev)
>> +{
>> +    return 0;
>> +}
>> +
>> +/** Stop device */
>> +static void
>> +armv8_crypto_pmd_stop(__rte_unused struct rte_cryptodev *dev)
>> +{
>> +}
>> +
>> +/** Close device */
>> +static int
>> +armv8_crypto_pmd_close(__rte_unused struct rte_cryptodev *dev)
>> +{
>> +    return 0;
>> +}
>> +
>> +
>> +/** Get device statistics */
>> +static void
>> +armv8_crypto_pmd_stats_get(struct rte_cryptodev *dev,
>> +        struct rte_cryptodev_stats *stats)
>> +{
>> +    int qp_id;
>> +
>> +    for (qp_id = 0; qp_id < dev->data->nb_queue_pairs; qp_id++) {
>> +        struct armv8_crypto_qp *qp = dev->data->queue_pairs[qp_id];
>> +
>> +        stats->enqueued_count += qp->stats.enqueued_count;
>> +        stats->dequeued_count += qp->stats.dequeued_count;
>> +
>> +        stats->enqueue_err_count += qp->stats.enqueue_err_count;
>> +        stats->dequeue_err_count += qp->stats.dequeue_err_count;
>> +    }
>> +}
>> +
>> +/** Reset device statistics */
>> +static void
>> +armv8_crypto_pmd_stats_reset(struct rte_cryptodev *dev)
>> +{
>> +    int qp_id;
>> +
>> +    for (qp_id = 0; qp_id < dev->data->nb_queue_pairs; qp_id++) {
>> +        struct armv8_crypto_qp *qp = dev->data->queue_pairs[qp_id];
>> +
>> +        memset(&qp->stats, 0, sizeof(qp->stats));
>> +    }
>> +}
>> +
>> +
>> +/** Get device info */
>> +static void
>> +armv8_crypto_pmd_info_get(struct rte_cryptodev *dev,
>> +        struct rte_cryptodev_info *dev_info)
>> +{
>> +    struct armv8_crypto_private *internals = dev->data->dev_private;
>> +
>> +    if (dev_info != NULL) {
>> +        dev_info->dev_type = dev->dev_type;
>> +        dev_info->feature_flags = dev->feature_flags;
>> +        dev_info->capabilities = armv8_crypto_pmd_capabilities;
>> +        dev_info->max_nb_queue_pairs = internals->max_nb_qpairs;
>> +        dev_info->sym.max_nb_sessions = internals->max_nb_sessions;
>> +    }
>> +}
>> +
>> +/** Release queue pair */
>> +static int
>> +armv8_crypto_pmd_qp_release(struct rte_cryptodev *dev, uint16_t qp_id)
>> +{
>> +
>> +    if (dev->data->queue_pairs[qp_id] != NULL) {
>> +        rte_free(dev->data->queue_pairs[qp_id]);
>> +        dev->data->queue_pairs[qp_id] = NULL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/** set a unique name for the queue pair based on it's name, dev_id
>> and qp_id */
>> +static int
>
>> +armv8_crypto_pmd_qp_set_unique_name(struct rte_cryptodev *dev,
>> +        struct armv8_crypto_qp *qp)
>> +{
>> +    unsigned int n;
>> +
>> +    n = snprintf(qp->name, sizeof(qp->name),
>> "armv8_crypto_pmd_%u_qp_%u",
>> +            dev->data->dev_id, qp->id);
>> +
>> +    if (n > sizeof(qp->name))
>> +        return -1;
>> +
>> +    return 0;
>> +}
>> +
>> +
>> +/** Create a ring to place processed operations on */
>> +static struct rte_ring *
>> +armv8_crypto_pmd_qp_create_processed_ops_ring(struct armv8_crypto_qp
>> *qp,
>> +        unsigned int ring_size, int socket_id)
>> +{
>> +    struct rte_ring *r;
>> +
>> +    r = rte_ring_lookup(qp->name);
>> +    if (r) {
>> +        if (r->prod.size >= ring_size) {
>> +            ARMV8_CRYPTO_LOG_INFO(
>> +                "Reusing existing ring %s for processed ops",
>> +                 qp->name);
>> +            return r;
>> +        }
>> +
>> +        ARMV8_CRYPTO_LOG_ERR(
>> +            "Unable to reuse existing ring %s for processed ops",
>> +             qp->name);
>> +        return NULL;
>> +    }
>> +
>> +    return rte_ring_create(qp->name, ring_size, socket_id,
>> +            RING_F_SP_ENQ | RING_F_SC_DEQ);
>> +}
>> +
>> +
>> +/** Setup a queue pair */
>> +static int
>> +armv8_crypto_pmd_qp_setup(struct rte_cryptodev *dev, uint16_t qp_id,
>> +        const struct rte_cryptodev_qp_conf *qp_conf,
>> +         int socket_id)
>> +{
>> +    struct armv8_crypto_qp *qp = NULL;
>> +
>> +    /* Free memory prior to re-allocation if needed. */
>> +    if (dev->data->queue_pairs[qp_id] != NULL)
>> +        armv8_crypto_pmd_qp_release(dev, qp_id);
>> +
>> +    /* Allocate the queue pair data structure. */
>> +    qp = rte_zmalloc_socket("ARMv8 PMD Queue Pair", sizeof(*qp),
>> +                    RTE_CACHE_LINE_SIZE, socket_id);
>> +    if (qp == NULL)
>> +        return -ENOMEM;
>> +
>> +    qp->id = qp_id;
>> +    dev->data->queue_pairs[qp_id] = qp;
>> +
>> +    if (armv8_crypto_pmd_qp_set_unique_name(dev, qp) != 0)
>> +        goto qp_setup_cleanup;
>> +
>> +    qp->processed_ops =
>> armv8_crypto_pmd_qp_create_processed_ops_ring(qp,
>> +            qp_conf->nb_descriptors, socket_id);
>> +    if (qp->processed_ops == NULL)
>> +        goto qp_setup_cleanup;
>> +
>> +    qp->sess_mp = dev->data->session_pool;
>> +
>> +    memset(&qp->stats, 0, sizeof(qp->stats));
>> +
>> +    return 0;
>> +
>> +qp_setup_cleanup:
>> +    if (qp)
>> +        rte_free(qp);
>> +
>> +    return -1;
>> +}
>> +
>> +/** Start queue pair */
>> +static int
>> +armv8_crypto_pmd_qp_start(__rte_unused struct rte_cryptodev *dev,
>> +        __rte_unused uint16_t queue_pair_id)
>> +{
>> +    return -ENOTSUP;
>> +}
>> +
>> +/** Stop queue pair */
>> +static int
>> +armv8_crypto_pmd_qp_stop(__rte_unused struct rte_cryptodev *dev,
>> +        __rte_unused uint16_t queue_pair_id)
>> +{
>> +    return -ENOTSUP;
>> +}
>> +
>> +/** Return the number of allocated queue pairs */
>> +static uint32_t
>> +armv8_crypto_pmd_qp_count(struct rte_cryptodev *dev)
>> +{
>> +    return dev->data->nb_queue_pairs;
>> +}
>> +
>> +/** Returns the size of the session structure */
>> +static unsigned
>> +armv8_crypto_pmd_session_get_size(struct rte_cryptodev *dev
>> __rte_unused)
>> +{
>> +    return sizeof(struct armv8_crypto_session);
>> +}
>> +
>> +/** Configure the session from a crypto xform chain */
>> +static void *
>> +armv8_crypto_pmd_session_configure(struct rte_cryptodev *dev
>> __rte_unused,
>> +        struct rte_crypto_sym_xform *xform, void *sess)
>> +{
>> +    if (unlikely(sess == NULL)) {
>> +        ARMV8_CRYPTO_LOG_ERR("invalid session struct");
>> +        return NULL;
>> +    }
>> +
>> +    if (armv8_crypto_set_session_parameters(
>> +            sess, xform) != 0) {
>> +        ARMV8_CRYPTO_LOG_ERR("failed configure session parameters");
>> +        return NULL;
>> +    }
>> +
>> +    return sess;
>> +}
>> +
>> +/** Clear the memory of session so it doesn't leave key material
>> behind */
>> +static void
>> +armv8_crypto_pmd_session_clear(struct rte_cryptodev *dev __rte_unused,
>> +                void *sess)
>> +{
>> +
>> +    /* Zero out the whole structure */
>> +    if (sess)
>> +        memset(sess, 0, sizeof(struct armv8_crypto_session));
>> +}
>> +
>> +struct rte_cryptodev_ops armv8_crypto_pmd_ops = {
>> +        .dev_configure        = armv8_crypto_pmd_config,
>> +        .dev_start        = armv8_crypto_pmd_start,
>> +        .dev_stop        = armv8_crypto_pmd_stop,
>> +        .dev_close        = armv8_crypto_pmd_close,
>> +
>> +        .stats_get        = armv8_crypto_pmd_stats_get,
>> +        .stats_reset        = armv8_crypto_pmd_stats_reset,
>> +
>> +        .dev_infos_get        = armv8_crypto_pmd_info_get,
>> +
>> +        .queue_pair_setup    = armv8_crypto_pmd_qp_setup,
>> +        .queue_pair_release    = armv8_crypto_pmd_qp_release,
>> +        .queue_pair_start    = armv8_crypto_pmd_qp_start,
>> +        .queue_pair_stop    = armv8_crypto_pmd_qp_stop,
>> +        .queue_pair_count    = armv8_crypto_pmd_qp_count,
>> +
>> +        .session_get_size    = armv8_crypto_pmd_session_get_size,
>> +        .session_configure    = armv8_crypto_pmd_session_configure,
>> +        .session_clear        = armv8_crypto_pmd_session_clear
>> +};
>> +
>> +struct rte_cryptodev_ops *rte_armv8_crypto_pmd_ops =
>> &armv8_crypto_pmd_ops;
>> diff --git a/drivers/crypto/armv8/rte_armv8_pmd_private.h
>> b/drivers/crypto/armv8/rte_armv8_pmd_private.h
>> new file mode 100644
>> index 0000000..fe46cde
>> --- /dev/null
>> +++ b/drivers/crypto/armv8/rte_armv8_pmd_private.h
>> @@ -0,0 +1,211 @@
>> +/*
>> + *   BSD LICENSE
>> + *
>> + *   Copyright (C) Cavium networks Ltd. 2017.
>> + *
>> + *   Redistribution and use in source and binary forms, with or without
>> + *   modification, are permitted provided that the following conditions
>> + *   are met:
>> + *
>> + *     * Redistributions of source code must retain the above copyright
>> + *       notice, this list of conditions and the following disclaimer.
>> + *     * Redistributions in binary form must reproduce the above
>> copyright
>> + *       notice, this list of conditions and the following disclaimer in
>> + *       the documentation and/or other materials provided with the
>> + *       distribution.
>> + *     * Neither the name of Cavium networks nor the names of its
>> + *       contributors may be used to endorse or promote products derived
>> + *       from this software without specific prior written permission.
>> + *
>> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
>> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
>> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
>> FITNESS FOR
>> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
>> COPYRIGHT
>> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
>> INCIDENTAL,
>> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>> USE,
>> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>> ON ANY
>> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
>> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
>> THE USE
>> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
>> DAMAGE.
>> + */
>> +
>> +#ifndef _RTE_ARMV8_PMD_PRIVATE_H_
>> +#define _RTE_ARMV8_PMD_PRIVATE_H_
>> +
>> +#define ARMV8_CRYPTO_LOG_ERR(fmt, args...) \
>> +    RTE_LOG(ERR, CRYPTODEV, "[%s] %s() line %u: " fmt "\n",  \
>> +            RTE_STR(CRYPTODEV_NAME_ARMV8_CRYPTO_PMD), \
>> +            __func__, __LINE__, ## args)
>> +
>> +#ifdef RTE_LIBRTE_ARMV8_CRYPTO_DEBUG
>> +#define ARMV8_CRYPTO_LOG_INFO(fmt, args...) \
>> +    RTE_LOG(INFO, CRYPTODEV, "[%s] %s() line %u: " fmt "\n", \
>> +            RTE_STR(CRYPTODEV_NAME_ARMV8_CRYPTO_PMD), \
>> +            __func__, __LINE__, ## args)
>> +
>> +#define ARMV8_CRYPTO_LOG_DBG(fmt, args...) \
>> +    RTE_LOG(DEBUG, CRYPTODEV, "[%s] %s() line %u: " fmt "\n", \
>> +            RTE_STR(CRYPTODEV_NAME_ARMV8_CRYPTO_PMD), \
>> +            __func__, __LINE__, ## args)
>> +
>> +#define ARMV8_CRYPTO_ASSERT(con)                \
>> +do {                                \
>> +    if (!(con)) {                        \
>> +        rte_panic("%s(): "                \
>> +            con "condition failed, line %u", __func__);    \
>> +    }                            \
>> +} while (0)
>> +
>> +#else
>> +#define ARMV8_CRYPTO_LOG_INFO(fmt, args...)
>> +#define ARMV8_CRYPTO_LOG_DBG(fmt, args...)
>> +#define ARMV8_CRYPTO_ASSERT(con)
>> +#endif
>> +
>> +#define NBBY        8        /* Number of bits in a byte */
>
> is it being used somewhere?

The intention was to use in in the line below. Fixed.

>
>> +#define BYTE_LENGTH(x)    ((x) / 8)    /* Number of bytes in x (roun
>> down) */
>
> "round down"  instead of "roun down"

Thanks. Fixed.

>
>> +
>> +/** ARMv8 operation order mode enumerator */
>> +enum armv8_crypto_chain_order {
>> +    ARMV8_CRYPTO_CHAIN_CIPHER_AUTH,
>> +    ARMV8_CRYPTO_CHAIN_AUTH_CIPHER,
>> +    ARMV8_CRYPTO_CHAIN_NOT_SUPPORTED,
>> +    ARMV8_CRYPTO_CHAIN_LIST_END = ARMV8_CRYPTO_CHAIN_NOT_SUPPORTED
>> +};
>> +
>> +/** ARMv8 cipher operation enumerator */
>> +enum armv8_crypto_cipher_operation {
>> +    ARMV8_CRYPTO_CIPHER_OP_ENCRYPT = RTE_CRYPTO_CIPHER_OP_ENCRYPT,
>> +    ARMV8_CRYPTO_CIPHER_OP_DECRYPT = RTE_CRYPTO_CIPHER_OP_DECRYPT,
>> +    ARMV8_CRYPTO_CIPHER_OP_NOT_SUPPORTED,
>> +    ARMV8_CRYPTO_CIPHER_OP_LIST_END =
>> ARMV8_CRYPTO_CIPHER_OP_NOT_SUPPORTED
>> +};
>> +
>> +enum armv8_crypto_cipher_keylen {
>> +    ARMV8_CRYPTO_CIPHER_KEYLEN_128,
>> +    ARMV8_CRYPTO_CIPHER_KEYLEN_192,
>> +    ARMV8_CRYPTO_CIPHER_KEYLEN_256,
>> +    ARMV8_CRYPTO_CIPHER_KEYLEN_NOT_SUPPORTED,
>> +    ARMV8_CRYPTO_CIPHER_KEYLEN_LIST_END =
>> +        ARMV8_CRYPTO_CIPHER_KEYLEN_NOT_SUPPORTED
>> +};
>> +
>> +/** ARMv8 auth mode enumerator */
>> +enum armv8_crypto_auth_mode {
>> +    ARMV8_CRYPTO_AUTH_AS_AUTH,
>> +    ARMV8_CRYPTO_AUTH_AS_HMAC,
>> +    ARMV8_CRYPTO_AUTH_AS_CIPHER,
>> +    ARMV8_CRYPTO_AUTH_NOT_SUPPORTED,
>> +    ARMV8_CRYPTO_AUTH_LIST_END = ARMV8_CRYPTO_AUTH_NOT_SUPPORTED
>> +};
>> +
>> +#define CRYPTO_ORDER_MAX        ARMV8_CRYPTO_CHAIN_LIST_END
>> +#define CRYPTO_CIPHER_OP_MAX        ARMV8_CRYPTO_CIPHER_OP_LIST_END
>> +#define CRYPTO_CIPHER_KEYLEN_MAX    ARMV8_CRYPTO_CIPHER_KEYLEN_LIST_END
>> +#define CRYPTO_CIPHER_MAX        RTE_CRYPTO_CIPHER_LIST_END
>> +#define CRYPTO_AUTH_MAX            RTE_CRYPTO_AUTH_LIST_END
>> +
>> +#define HMAC_IPAD_VALUE            (0x36)
>> +#define HMAC_OPAD_VALUE            (0x5C)
>> +
>> +#define SHA256_AUTH_KEY_LENGTH        (BYTE_LENGTH(256))
>> +#define SHA256_BLOCK_SIZE        (BYTE_LENGTH(512))
>> +
>> +#define SHA1_AUTH_KEY_LENGTH        (BYTE_LENGTH(160))
>> +#define SHA1_BLOCK_SIZE            (BYTE_LENGTH(512))
>> +
>> +#define SHA_AUTH_KEY_MAX        SHA256_AUTH_KEY_LENGTH
>> +#define SHA_BLOCK_MAX            SHA256_BLOCK_SIZE
>> +
>> +typedef int (*crypto_func_t)(uint8_t *, uint8_t *, uint64_t,
>> +                uint8_t *, uint8_t *, uint64_t,
>> +                crypto_arg_t *);
>> +
>> +typedef void (*crypto_key_sched_t)(uint8_t *, const uint8_t *);
>> +
>> +/** private data structure for each ARMv8 crypto device */
>> +struct armv8_crypto_private {
>> +    unsigned int max_nb_qpairs;
>> +    /**< Max number of queue pairs */
>> +    unsigned int max_nb_sessions;
>> +    /**< Max number of sessions */
>> +};
>> +
>> +/** ARMv8 crypto queue pair */
>> +struct armv8_crypto_qp {
>> +    uint16_t id;
>> +    /**< Queue Pair Identifier */
>> +    char name[RTE_CRYPTODEV_NAME_LEN];
>> +    /**< Unique Queue Pair Name */
>> +    struct rte_ring *processed_ops;
>> +    /**< Ring for placing process packets */
>> +    struct rte_mempool *sess_mp;
>> +    /**< Session Mempool */
>> +    struct rte_cryptodev_stats stats;
>> +    /**< Queue pair statistics */
>> +} __rte_cache_aligned;
>> +
>> +/** ARMv8 crypto private session structure */
>> +struct armv8_crypto_session {
>> +    enum armv8_crypto_chain_order chain_order;
>> +    /**< chain order mode */
>> +    crypto_func_t crypto_func;
>> +    /**< cryptographic function to use for this session */
>> +
>> +    /** Cipher Parameters */
>> +    struct {
>> +        enum rte_crypto_cipher_operation direction;
>> +        /**< cipher operation direction */
>> +        enum rte_crypto_cipher_algorithm algo;
>> +        /**< cipher algorithm */
>> +        int iv_len;
>> +        /**< IV length */
>> +
>> +        struct {
>> +            uint8_t data[256];
>> +            /**< key data */
>> +            size_t length;
>> +            /**< key length in bytes */
>> +        } key;
>> +
>> +        crypto_key_sched_t key_sched;
>> +        /**< Key schedule function */
>> +    } cipher;
>> +
>> +    /** Authentication Parameters */
>> +    struct {
>> +        enum rte_crypto_auth_operation operation;
>> +        /**< auth operation generate or verify */
>> +        enum armv8_crypto_auth_mode mode;
>> +        /**< auth operation mode */
>> +
>> +        union {
>> +            struct {
>> +                /* Add data if needed */
>> +            } auth;
>> +
>> +            struct {
>> +                uint8_t i_key_pad[SHA_BLOCK_MAX]
>> +                            __rte_cache_aligned;
>> +                /**< inner pad (max supported block length) */
>> +                uint8_t o_key_pad[SHA_BLOCK_MAX]
>> +                            __rte_cache_aligned;
>> +                /**< outer pad (max supported block length) */
>> +                uint8_t key[SHA_AUTH_KEY_MAX];
>> +                /**< HMAC key (max supported length)*/
>> +            } hmac;
>> +        };
>> +    } auth;
>> +
>> +} __rte_cache_aligned;
>> +
>> +/** Set and validate ARMv8 crypto session parameters */
>> +extern int armv8_crypto_set_session_parameters(
>> +        struct armv8_crypto_session *sess,
>> +        const struct rte_crypto_sym_xform *xform);
>> +
>> +/** device specific operations function pointer structure */
>> +extern struct rte_cryptodev_ops *rte_armv8_crypto_pmd_ops;
>> +
>> +#endif /* _RTE_ARMV8_PMD_PRIVATE_H_ */
>> diff --git a/drivers/crypto/armv8/rte_armv8_pmd_version.map
>> b/drivers/crypto/armv8/rte_armv8_pmd_version.map
>> new file mode 100644
>> index 0000000..1f84b68
>> --- /dev/null
>> +++ b/drivers/crypto/armv8/rte_armv8_pmd_version.map
>> @@ -0,0 +1,3 @@
>> +DPDK_17.02 {
>> +    local: *;
>> +};
>>
>
>

^ permalink raw reply

* Re: [PATCH v3 3/8] crypto/armv8: add PMD optimized for ARMv8 processors
From: Zbigniew Bodek @ 2017-01-13 19:09 UTC (permalink / raw)
  To: Jianbo Liu; +Cc: dev, pablo.de.lara.guarch, Declan Doherty, Jerin Jacob
In-Reply-To: <CAP4Qi3905Q_tAqF5XZnXncePbMg4Jd2fhJ5GF4HvXOSiURgRfA@mail.gmail.com>



On 13.01.2017 08:41, Jianbo Liu wrote:
> On 12 January 2017 at 21:12, Zbigniew Bodek
> <zbigniew.bodek@caviumnetworks.com> wrote:
>> Hello  Jianbo Liu,
>>
>> Thanks for the review. Please check my answers in-line.
>>
>> Kind regards
>> Zbigniew
>>
>>
>> On 06.01.2017 03:45, Jianbo Liu wrote:
>>>
>>> On 5 January 2017 at 01:33,  <zbigniew.bodek@caviumnetworks.com> wrote:
>>>>
>>>> From: Zbigniew Bodek <zbigniew.bodek@caviumnetworks.com>
>>>>
>>>> This patch introduces crypto poll mode driver
>>>> using ARMv8 cryptographic extensions.
>>>> CPU compatibility with this driver is detected in
>>>> run-time and virtual crypto device will not be
>>>> created if CPU doesn't provide:
>>>> AES, SHA1, SHA2 and NEON.
>>>>
>>>> This PMD is optimized to provide performance boost
>>>> for chained crypto operations processing,
>>>> such as encryption + HMAC generation,
>>>> decryption + HMAC validation. In particular,
>>>> cipher only or hash only operations are
>>>> not provided.
>>>>
>>>> The driver currently supports AES-128-CBC
>>>> in combination with: SHA256 HMAC and SHA1 HMAC
>>>> and relies on the external armv8_crypto library:
>>>> https://github.com/caviumnetworks/armv8_crypto
>>>>
>>>
>>> It's standalone lib. I think you should change the following line in
>>> its Makefile, so not depend on DPDK.
>>> "include $(RTE_SDK)/mk/rte.lib.mk"
>>>
>>>> This patch adds driver's code only and does
>>>> not include it in the build system.
>>>>
>>>> Signed-off-by: Zbigniew Bodek <zbigniew.bodek@caviumnetworks.com>
>>>> ---
>>>>  drivers/crypto/armv8/Makefile                  |  73 ++
>>>>  drivers/crypto/armv8/rte_armv8_pmd.c           | 926
>>>> +++++++++++++++++++++++++
>>>>  drivers/crypto/armv8/rte_armv8_pmd_ops.c       | 369 ++++++++++
>>>>  drivers/crypto/armv8/rte_armv8_pmd_private.h   | 211 ++++++
>>>>  drivers/crypto/armv8/rte_armv8_pmd_version.map |   3 +
>>>>  5 files changed, 1582 insertions(+)
>>>>  create mode 100644 drivers/crypto/armv8/Makefile
>>>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd.c
>>>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_ops.c
>>>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_private.h
>>>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_version.map
>>>>
> .....
>
>>>> +       /* Select auth algo */
>>>> +       switch (auth_xform->auth.algo) {
>>>> +       /* Cover supported hash algorithms */
>>>> +       case RTE_CRYPTO_AUTH_SHA256:
>>>> +               aalg = auth_xform->auth.algo;
>>>> +               sess->auth.mode = ARMV8_CRYPTO_AUTH_AS_AUTH;
>>>> +               break;
>>>> +       case RTE_CRYPTO_AUTH_SHA1_HMAC:
>>>> +       case RTE_CRYPTO_AUTH_SHA256_HMAC: /* Fall through */
>>>> +               aalg = auth_xform->auth.algo;
>>>> +               sess->auth.mode = ARMV8_CRYPTO_AUTH_AS_HMAC;
>>>> +               break;
>>>> +       default:
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       /* Verify supported key lengths and extract proper algorithm */
>>>> +       switch (cipher_xform->cipher.key.length << 3) {
>>>> +       case 128:
>>>> +               sess->crypto_func =
>>>> +                               CRYPTO_GET_ALGO(order, cop, calg, aalg,
>>>> 128);
>>>> +               sess->cipher.key_sched =
>>>> +                               CRYPTO_GET_KEY_SCHED(cop, calg, 128);
>>>> +               break;
>>>> +       case 192:
>>>> +               sess->crypto_func =
>>>> +                               CRYPTO_GET_ALGO(order, cop, calg, aalg,
>>>> 192);
>>>> +               sess->cipher.key_sched =
>>>> +                               CRYPTO_GET_KEY_SCHED(cop, calg, 192);
>>>> +               break;
>>>> +       case 256:
>>>> +               sess->crypto_func =
>>>> +                               CRYPTO_GET_ALGO(order, cop, calg, aalg,
>>>> 256);
>>>> +               sess->cipher.key_sched =
>>>> +                               CRYPTO_GET_KEY_SCHED(cop, calg, 256);
>>>> +               break;
>>>> +       default:
>>>> +               sess->crypto_func = NULL;
>>>> +               sess->cipher.key_sched = NULL;
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       if (unlikely(sess->crypto_func == NULL)) {
>>>> +               /*
>>>> +                * If we got here that means that there must be a bug
>>>
>>>
>>> Since AES-128-CBC is only supported in your patch. It means that
>>> crypto_func could be NULL according to the switch above if
>>> cipher.key.length > 128?
>>
>>
>> Yes. Instead of checking for key lengths in a similar way that we check for
>> algorithms, etc. we just fail when we don't find appropriate function. Do
>> you suggest that this should be changed?
>>
>
> I mean to return error directly if length is not 128 in the above
> switch, so this "if" is no necessary.

OK. Done. Will resend patches soon.

>
>>
>>>
>>>> +                * in the algorithms selection above. Nevertheless keep
>>>> +                * it here to catch bug immediately and avoid NULL
>>>> pointer
>>>> +                * dereference in OPs processing.
>>>> +                */
>>>> +               ARMV8_CRYPTO_LOG_ERR(
>>>> +                       "No appropriate crypto function for given
>>>> parameters");
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       /* Set up cipher session prerequisites */
>>>> +       if (cipher_set_prerequisites(sess, cipher_xform) != 0)
>>>> +               return -EINVAL;
>>>> +
>>>> +       /* Set up authentication session prerequisites */
>>>> +       if (auth_set_prerequisites(sess, auth_xform) != 0)
>>>> +               return -EINVAL;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>
>>>
>>> ....
>>>
>>>> diff --git a/drivers/crypto/armv8/rte_armv8_pmd_ops.c
>>>> b/drivers/crypto/armv8/rte_armv8_pmd_ops.c
>>>> new file mode 100644
>>>> index 0000000..2bf6475
>>>> --- /dev/null
>>>> +++ b/drivers/crypto/armv8/rte_armv8_pmd_ops.c
>>>> @@ -0,0 +1,369 @@
>>>> +/*
>>>> + *   BSD LICENSE
>>>> + *
>>>> + *   Copyright (C) Cavium networks Ltd. 2017.
>>>> + *
>>>> + *   Redistribution and use in source and binary forms, with or without
>>>> + *   modification, are permitted provided that the following conditions
>>>> + *   are met:
>>>> + *
>>>> + *     * Redistributions of source code must retain the above copyright
>>>> + *       notice, this list of conditions and the following disclaimer.
>>>> + *     * Redistributions in binary form must reproduce the above
>>>> copyright
>>>> + *       notice, this list of conditions and the following disclaimer in
>>>> + *       the documentation and/or other materials provided with the
>>>> + *       distribution.
>>>> + *     * Neither the name of Cavium networks nor the names of its
>>>> + *       contributors may be used to endorse or promote products derived
>>>> + *       from this software without specific prior written permission.
>>>> + *
>>>> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
>>>> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
>>>> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
>>>> FOR
>>>> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
>>>> COPYRIGHT
>>>> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
>>>> INCIDENTAL,
>>>> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>>>> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>>>> USE,
>>>> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
>>>> ANY
>>>> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
>>>> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
>>>> USE
>>>> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
>>>> DAMAGE.
>>>> + */
>>>> +
>>>> +#include <string.h>
>>>> +
>>>> +#include <rte_common.h>
>>>> +#include <rte_malloc.h>
>>>> +#include <rte_cryptodev_pmd.h>
>>>> +
>>>> +#include "armv8_crypto_defs.h"
>>>> +
>>>> +#include "rte_armv8_pmd_private.h"
>>>> +
>>>> +static const struct rte_cryptodev_capabilities
>>>> +       armv8_crypto_pmd_capabilities[] = {
>>>> +       {       /* SHA1 HMAC */
>>>> +               .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
>>>> +                       {.sym = {
>>>> +                               .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
>>>> +                               {.auth = {
>>>> +                                       .algo =
>>>> RTE_CRYPTO_AUTH_SHA1_HMAC,
>>>> +                                       .block_size = 64,
>>>> +                                       .key_size = {
>>>> +                                               .min = 16,
>>>> +                                               .max = 128,
>>>> +                                               .increment = 0
>>>> +                                       },
>>>> +                                       .digest_size = {
>>>> +                                               .min = 20,
>>>> +                                               .max = 20,
>>>> +                                               .increment = 0
>>>> +                                       },
>>>> +                                       .aad_size = { 0 }
>>>> +                               }, }
>>>> +                       }, }
>>>> +       },
>>>> +       {       /* SHA256 HMAC */
>>>> +               .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
>>>> +                       {.sym = {
>>>> +                               .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
>>>> +                               {.auth = {
>>>> +                                       .algo =
>>>> RTE_CRYPTO_AUTH_SHA256_HMAC,
>>>> +                                       .block_size = 64,
>>>> +                                       .key_size = {
>>>> +                                               .min = 16,
>>>> +                                               .max = 128,
>>>> +                                               .increment = 0
>>>> +                                       },
>>>> +                                       .digest_size = {
>>>> +                                               .min = 32,
>>>> +                                               .max = 32,
>>>> +                                               .increment = 0
>>>> +                                       },
>>>> +                                       .aad_size = { 0 }
>>>> +                               }, }
>>>> +                       }, }
>>>> +       },
>>>> +       {       /* AES CBC */
>>>> +               .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
>>>> +                       {.sym = {
>>>> +                               .xform_type =
>>>> RTE_CRYPTO_SYM_XFORM_CIPHER,
>>>> +                               {.cipher = {
>>>> +                                       .algo =
>>>> RTE_CRYPTO_CIPHER_AES_CBC,
>>>> +                                       .block_size = 16,
>>>> +                                       .key_size = {
>>>> +                                               .min = 16,
>>>> +                                               .max = 16,
>>>> +                                               .increment = 0
>>>> +                                       },
>>>> +                                       .iv_size = {
>>>> +                                               .min = 16,
>>>> +                                               .max = 16,
>>>> +                                               .increment = 0
>>>> +                                       }
>>>> +                               }, }
>>>> +                       }, }
>>>> +       },
>>>> +
>>>
>>>
>>> It's strange that you defined aes and hmac here, but not implemented
>>> them, though their combinations are implemented.
>>> Will you add later?
>>
>>
>> We may add standalone algorithms in the future but those ops here are not
>> for that purpose. I thought that since there is no chained operations
>> capability we should export what we can do even though that it will work
>> (mean not return error) only if the operations are chained.
>> Do you have some other suggestion?
>>
>
> Nothing special. Either implement them later, or add new chained ops
> (is that possible?)
> BTW, can you explain what optimization you have done, so I can better
> understand your asm code, thanks!

Yes. The optimized assembly code is utilizing locality of reference 
while doing encryption/decryption as well as hash at the same time 
rather than one after another. Also, significant parts of the code are 
arranged for best instructions pipelining.
Some parts of the implementation such as key schedule are written in a 
way that uses NEON and crypto instructions to speed up operations needed 
for key expansion.

>
>>
>>>
>>>> +       RTE_CRYPTODEV_END_OF_CAPABILITIES_LIST()
>>>> +};
>>>> +
>>>> +

^ permalink raw reply

* Re: [PATCH v3 0/8] Add crypto PMD optimized for ARMv8
From: Zbigniew Bodek @ 2017-01-13 18:59 UTC (permalink / raw)
  To: Hemant Agrawal, dev; +Cc: pablo.de.lara.guarch, declan.doherty, jerin.jacob
In-Reply-To: <b9eaec28-8c04-9207-c0e3-c7915c70f2cf@nxp.com>

Hello Hemant,

Thank you for your remarks and comments. Please check my answer below.

Kind regards
Zbigniew

On 13.01.2017 09:07, Hemant Agrawal wrote:
> On 1/4/2017 11:03 PM, zbigniew.bodek@caviumnetworks.com wrote:
>> From: Zbigniew Bodek <zbigniew.bodek@caviumnetworks.com>
>>
>> Introduce crypto poll mode driver using ARMv8
>> cryptographic extensions. This PMD is optimized
>> to provide performance boost for chained
>> crypto operations processing, such as:
>> * encryption + HMAC generation
>> * decryption + HMAC validation.
>> In particular, cipher only or hash only
>> operations are not provided.
>
> Do you have a plan to add the crypto only, auth/hash only support into
> this driver?

OpenSSL driver is already implementing that and it is optimized for ARMv8.

> Also, do you plan to add additional cases w.r.t supported by other
> crypto driver?

We may do it in the future but this depends on our resource availability.

>
>> Performance gain can be observed in tests
>> against OpenSSL PMD which also uses ARM
>> crypto extensions for packets processing.
>>
>> Exemplary crypto performance tests comparison:
>>
>> cipher_hash. cipher algo: AES_CBC
>> auth algo: SHA1_HMAC cipher key size=16.
>> burst_size: 64 ops
>>
>> ARMv8 PMD improvement over OpenSSL PMD
>> (Optimized for ARMv8 cipher only and hash
>> only cases):
>>
>> Buffer
>> Size(B)   OPS(M)      Throughput(Gbps)
>> 64        729 %        742 %
>> 128       577 %        592 %
>> 256       483 %        476 %
>> 512       336 %        351 %
>> 768       300 %        286 %
>> 1024      263 %        250 %
>> 1280      225 %        229 %
>> 1536      214 %        213 %
>> 1792      186 %        203 %
>> 2048      200 %        193 %
>>
>> The driver currently supports AES-128-CBC
>> in combination with: SHA256 HMAC and SHA1 HMAC.
>> The core crypto functionality of this driver is
>> provided by the external armv8_crypto library
>> that can be downloaded from the Cavium repository:
>> https://github.com/caviumnetworks/armv8_crypto
>>
>> CPU compatibility with this virtual device
>> is detected in run-time and virtual crypto
>> device will not be created if CPU doesn't
>> provide AES, SHA1, SHA2 and NEON.
>>
>> The functionality and performance of this
>> code can be tested using generic test application
>> with the following commands:
>> * cryptodev_sw_armv8_autotest
>> * cryptodev_sw_armv8_perftest
>> New test vectors and cases have been added
>> to the general pool. In particular SHA1 and
>> SHA256 HMAC for short cases were introduced.
>> This is because low-level ARM assembly code
>> is using different code paths for long and
>> short data sets, so in order to test the
>> mentioned driver correctly, two different
>> data sets need to be provided.
>>
>> ---
>> v3:
>> * Addressed review remarks
>> * Moved low-level assembly code to the external library
>> * Removed SHA256 MAC cases
>> * Various fixes: interface to the library, digest destination
>>   and source address interpreting, missing mbuf manipulations.
>>
>> v2:
>> * Fixed checkpatch warnings
>> * Divide patches into smaller logical parts
>>
>> Zbigniew Bodek (8):
>>   mk: fix build of assembly files for ARM64
>>   lib: add cryptodev type for the upcoming ARMv8 PMD
>>   crypto/armv8: add PMD optimized for ARMv8 processors
>>   mk/crypto/armv8: add PMD to the build system
>>   doc/armv8: update documentation about crypto PMD
>>   crypto/armv8: enable ARMv8 PMD in the configuration
>>   crypto/armv8: update MAINTAINERS entry for ARMv8 crypto
>>   app/test: add ARMv8 crypto tests and test vectors
>>
>>  MAINTAINERS                                    |   6 +
>>  app/test/test_cryptodev.c                      |  63 ++
>>  app/test/test_cryptodev_aes_test_vectors.h     | 144 +++-
>>  app/test/test_cryptodev_blockcipher.c          |   4 +
>>  app/test/test_cryptodev_blockcipher.h          |   1 +
>>  app/test/test_cryptodev_perf.c                 | 480 +++++++++++++
>>  config/common_base                             |   6 +
>>  doc/guides/cryptodevs/armv8.rst                |  96 +++
>>  doc/guides/cryptodevs/index.rst                |   1 +
>>  doc/guides/rel_notes/release_17_02.rst         |   5 +
>>  drivers/crypto/Makefile                        |   1 +
>>  drivers/crypto/armv8/Makefile                  |  73 ++
>>  drivers/crypto/armv8/rte_armv8_pmd.c           | 926
>> +++++++++++++++++++++++++
>>  drivers/crypto/armv8/rte_armv8_pmd_ops.c       | 369 ++++++++++
>>  drivers/crypto/armv8/rte_armv8_pmd_private.h   | 211 ++++++
>>  drivers/crypto/armv8/rte_armv8_pmd_version.map |   3 +
>>  lib/librte_cryptodev/rte_cryptodev.h           |   3 +
>>  mk/arch/arm64/rte.vars.mk                      |   1 -
>>  mk/rte.app.mk                                  |   2 +
>>  mk/toolchain/gcc/rte.vars.mk                   |   6 +-
>>  20 files changed, 2390 insertions(+), 11 deletions(-)
>>  create mode 100644 doc/guides/cryptodevs/armv8.rst
>>  create mode 100644 drivers/crypto/armv8/Makefile
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd.c
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_ops.c
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_private.h
>>  create mode 100644 drivers/crypto/armv8/rte_armv8_pmd_version.map
>>
>
>

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox