From cc270bda890d4272477daa5264e47e05263c0967 Mon Sep 17 00:00:00 2001 From: ennucore Date: Thu, 12 Mar 2020 23:03:42 +0300 Subject: [PATCH] Init --- .gitignore | 1 + Cargo.lock | 307 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 +++ README.md | 85 +++++++++++++ scheme.fzz | Bin 0 -> 28521 bytes src/addresses.rs | 14 +++ src/ironforce.rs | 16 +++ src/lib.rs | 33 +++++ src/message.rs | 36 ++++++ src/transport.rs | 2 + 10 files changed, 508 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 scheme.fzz create mode 100644 src/addresses.rs create mode 100644 src/ironforce.rs create mode 100644 src/lib.rs create mode 100644 src/message.rs create mode 100644 src/transport.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e420ee4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/* diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d3289b3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,307 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "alt_serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "curve25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ecdsa" +version = "0.5.0-pre" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "elliptic-curve 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signature 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "elliptic-curve" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "half" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ironforest" +version = "0.1.0" +dependencies = [ + "alt_serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ecdsa 0.5.0-pre (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_cbor 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_cbor" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "half 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "signature" +version = "1.0.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "subtle" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum alt_serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "03beeddedd09889b96def26f78ba46e34ffd9bdaaa33b2c980cbaa1d0e762686" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum ecdsa 0.5.0-pre (registry+https://github.com/rust-lang/crates.io-index)" = "1d7e523a6e90b7682c0857c1d26cf06f3a0224bc2dfb0766f5e35a0a71ad3dbe" +"checksum elliptic-curve 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01f69be7d1feb7a7a04f158aaf32c7deaa7604e9bd58145525e536438c4e5096" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum half 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f36b5f248235f45773d4944f555f83ea61fe07b18b561ccf99d7483d7381e54d" +"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +"checksum serde_cbor 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +"checksum signature 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "561619c00cf6a187ebfc21e46bc4c0ce4e4d5f67cd640e7b1c58d9c3754b38aa" +"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" +"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0795beb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ironforest" +version = "0.1.0" +authors = ["ennucore "] +edition = "2018" + + +[dependencies] +ecdsa = "0.5.0-pre" +curve25519-dalek = "2" +alt_serde = "1.0.104" +rand = "*" +serde = { version = "1.0", features = ["derive", "alloc"], default-features = false } +serde_cbor = { version = "0.11", default-features = false } diff --git a/README.md b/README.md new file mode 100644 index 0000000..d4376ca --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# IronForest + +__IronForest__ _(IF)_ is a decentralized P2P network. + + + +IronForest hardware network has two types of devices: digital (IronForest Digital Device, IFDD) and analog (IronForest Digital Device, IFAD) + +## IronForest tonnels + +Devices communicate through tonnels. Tonnel is a list of IF nodes' IDs: $ T(a_1, a_n)=[a_1, a_2, \,..., \, a_n] $, where $a_i$ is an id. _Short tonnel_ ($s(T)$) is one of the $min\_tonnels$ shortest paths (by time) between two nodes. Each IF node $X$ has its own list of short tonnels $X.TT$ ($\forall \, T \in X.TT\,\, X\in T$). It is obvious, that if $T(a_1, a_n)_p=A$ and $T(a_1, a_n)_q=B$ , then $T(A, B)=T(a_1,a_n)_{p:q}$. Therefore, if node $X$ has ID of node $Y$ somewhere in its list of tonnels, it knows a tonnel to $Y$. + +```mermaid +graph TD; +A --- E --- F --- B; +A---G---B; +E---Z---F; +``` + +_Here [A, G, B], [A, E, F, B], [A, E, Z, F, B] are tonnels from A to B, but the last one will not probably be short tonnel, because it will highly likely take more time for a packet to reach the $B$ node by this path._ + +Node $N$ is _known_ to node $B$ if node $B$ has node $N$ in its list of tonnels: $kn(N, B)=\exists T \in N.TT\,:\,B\in T$. + +On low-level, a also tonnel has its frequency $T.F$: IFADs retranslate signals on all frequencies from $F_2 \cup \{F_1\}$ and IFDDs set one of their transmitters to the $T.F$ frequency. + +#### Building a tonnel + +How does node $A$ build a tonnel to an unknown node $B$? + +$A$ sends a multicast message to the network. This message consists of type (tonnel request), destination $m.d=B$ and path (list) $m.P$ ($P_0=[A]$) and tonnel (empty for now). When another node $N$ receives message of this type, it appends its id to the path ($m.P+=[N]$) and resends this message either nobody if $N$ is a part of too much tonnels (this number is equal to the number of $F2$ radio modules on the IFDD board) either to all available nodes if node $B$ is not a known node to $N$ (if $!kn(N, B) = \nexists T \in N.TT: B\in T$) and message has no tonnel parameter either only to some nodes. Let's see the second variant more detailed. + +​ So, node $N$ receives a message $m$. If message has not empty tonnel parameter $m.T$, then $N$ remembers tonnel T, sends message to the next node in the tonnel ($m.T_{k+1}, k: m.T_k = N$). If node $B$($m.d$) is known to $N$ ($kn(N, B)$), then: + +1. $N$ checks if number of tonnels to $B$ is enough: $TT_2=\{T \in N.TT: B \in T\},\,\,|TT_2|>= tonnel\_num$, if not, then message is resent through all paths +2. $N$ sends message $m$ to $tonnel\_num$ of $TT_2$ ($TT_{2\,[1:tonnel\_num]}$) + + + +$M=[m_1, \,...,\,m_{tonnel\_num}]$ is a list of first $tonnel\_num$ messages that reached B + +When message $m_i \in M$ with updated path reaches $B$, $B$ remembers reversed of $m_i.P$ as the tonnel from $B$ to $A$: $T(A, B)=m.P$. $B$ decides frequency $T.F$ of tonnel (for hardware network). It sends a message with this frequency. to $A$ as multicast message using this tonnel. $A$ receives this tonnel and remembers it. Now $kn(A, B) \and kn(B, A)$. + +## IronForest Hardware Network + +There are two types of IFHN devices: analog devices (IFAD) and digital (IFDD). The analog devices are very cheap, because they are just a electronic circuits with analog components. They just re-translate all the signals instantly, without any delay. Digital devices are more complicated. They decode messages and then resend them with filtering and processing. Digital devices provide spam protection and logic. + + + +IronForest hardware0 network has two frequencies: one (_F1_) for multicast and a frequency range (F2) for tonnels. + +## IronForest analog device (IFAD) description + +IFADs work as retranslators. Analog devices receive the signal, amplify it and send it further. They do not react on signals weaker then some threshold. But the output strength of the signal is lower then it was when it was send by the previous device. Therefore, a packet will not go far only through IFADs. This also solves the problem of signals circulating through analog network being amplified every time. + +## IronForest digital device (IFDD) description + +IFDD can be connected to another device or to another network, and it will use available network for overlay. + +IFDD have radio transmitters: one for $F_1$ frequency and several transmitters for $F_2$ messages. Transmitters of the second type can be set to any frequency of the $F_2$ range using varicond/varicap. + +Digital device receives signal, amplifies it and demodulates. Then signal is processed by a micro-controller (MC): + +1. MC checks validity +2. Spam protection (PoW, for example) is done +3. If this is an $F2$ signal, check if it needs resending by this IFDD, resend by radio and overlays if needed +4. If this is an $F1$ signal, process it and resend by radio and overlays + +IFDD's MC has a list of known tonnels. If it is clear that the destination of a packet is a known node (known as a part of a tonnel), IFDD will not send packet to all available nodes, it will send it just like through a usual tonnel to this known node. + +## Calculating network speed and price for square kilometer + +_todo_ + + + +## Some schemes + +![image-20200119194630807](/home/ennucore/.config/Typora/typora-user-images/image-20200119194630807.png) + +http://www.diagram.com.ua/list/am-mod.shtml + +![Высоколинейный амплитудный модулятор](http://www.diagram.com.ua/li/am-mod1.gif) + +http://rtf.sfedu.ru/!rpru/files/upos04.pdf + diff --git a/scheme.fzz b/scheme.fzz new file mode 100644 index 0000000000000000000000000000000000000000..0f70716a6d7e1e7e8ee23285e43281424f9372d8 GIT binary patch literal 28521 zcmZs?Wl-B)^etSZSaEkL#oawP6ff@30>RxKiWGNucZ$2aYjFwg65J)1=l#!n@BMJU zoXqUWB?uk%9=1{V9%r%woBu2X|5@ z4(eh@8)n2<^4U~bOyW&vl|;JY?7t98e1eMchhHb@IczMQK$hs~hk5MnhTPFWz%dt_ zpx15i)!bWu$g}~gnBn@mkNex@!F&JOw83jgwQp@b%R9&rvY@mf-0=hkfhKuk9+dp( z4W2>oD2cpZ5k0P!{9IHcb2>a*e7tYU-p6l**XJ+Z+j(+2A5UIF3_nb zyG_vB!A;J4o9~?&i)}VIX9MJOpYL(i?$#W`GkD6=p22bf06~uL7uwtLcpk_AI4oVv zs0;>1EVfcYWVlhZxI=G7vN3bm)n&u;+g7GIFQ@Aw2g7_tM7HlOZ!E; z6BESuX8YdfYPOp6!gf;)9#Zu(15oo6^OGkhfvS{@~>4 z_09X$`xSUj$?tIi6w^9xduY%oLay#ARB3P*5DcSpe4d_` zAn#!C$0n5arDsk!Y`eWYX{YX;AKG8mSRyJeD(o&kHrWp;eOVDy;GyKZr|tk?NhgXGrzB!ugAL) zhh=JWPmhn6SKzDTkOQ|eG{)qcdyw0pp~u4m5r0=3aJ65*c49D#vZZl|9+-7;e)qP} zwaKfpZn|h8T2`!9xA9W3_FO%_c1Ocr4sAY3|yvEx*uQPuzcd%=goCIa_NF zd2myT5ng{SD@^rtP_1msl|ayDd_~LYTpry~nN=iJs(OFR;6Ukc{vPnAHkJ1I?}11~ z{oM8(sqbSC20YJel9KP`igfqLxG{Iv`XB!;!G)~`Snk%hBB11QLT+&vFb+7uM* z>h_q{x_jv~>+tO9Hp_n(FuR+6$-X%tOUIMAg0A2HcnzZ6OS?IqA~D$yWAGP zBKL78)+%}z$G@DF(Nc${^dtET)mlR5jk=DI5T%6)L|Jhtam}Arc1V&Nu4?I zuZ-qpR1SjFFf1sT-S&0J*HO6b&u`(U#T7P7nY)zhjqQ$Q|C5bBp7L z^V6yq#}#q+E$;x&G1(0X)v7}{4pZvgLWjYhenM*=w`v}rhQtgeWC0|}PpC@59fJ*g zAnF@lG;W4~*;k*dgS<{nuB;TzoS6Nq!E)s1i}t5W(2J&9%(+JL>}J-#*K=u_)tT^g*W}b|K0Wpz zJ~1vQyfd>39|o(Y5)cZL&3*luxiG`+!hQWh`rG-t)!X*_+*`#zvFF=IkpA6dIOyqy zXZMbWp}VuH%g3Sh{pzCo>0l0|DpW>x@3)eM$D`!t=e?S7Va{c+?tOE)UDlLYR{Po0 z3362`t~oOb*c8|2`;XvaC$Z)RPO8YZ&5 z;4#Hk?Y`wt&^7~}T+>#Le{|DHCDujx76h;^)R#2pI0B(PN28B z(5wo5dsnj9X$kM9kguyHf(Si#%fa#dlyK^&P%c;_YQq8qmV9|-Li@>oW9Q-j&t@%J z4_bHf$a_!rKsCc*EJkCg;dnlM_sp!=e1dG!smi-$0XL281o9S_^5SMhWmfFixcU0y zH3Aq!z%*7`JKS#A(eCq72AeDN)IyK;@&ypXV=#heppzz@5f?G z3iv=OzvsNV&z)^di;t5Cj;8gcF@JygG1aqHWIzRq0noCcPJg^xt*^-~LxtrG!ceGz zG%>f4O+u%*GZ>^3E18M~%n;^)LCffZ#18)!*Jl##ZO@%;?G3zppmAr_eO4^}??vV(eNvi^_ON z;VaT_k~+0K8BK$SmuvCP`TTcJ2yew89ZV!B1kZ;agpiK2r$e_&{~|2cVASWGinNmo zAz@Wm!CNe6aQyd?a2vmO#hc&|QFB=Z&XU$y%c0bW-SH3toQn#!;=B2JioV_T&e_}8 zCl19$rwdkr-MhJvER9ea4~>Wg+XKFAiPfiFnLiT(a&1DWWT~xx+!=)|54Xbj+(L(Y zv2IRihx{~SS+h%x~46u-35!7kNt}6wm|opSX>uODaFTl|6DP zPRSS$YCJ4^?+}Q(D4|_|NyqsbDO@1eoAce*3!{IP>@T^CeJ{M0hF{Qla%2hp+}FAk zRCJ(UCD1oh!Yohc8x^vcP>%Bv?RTTKI7)tJj_x=Y#dS5sJE;hwEu1NP12cKt?)gNN zk>f{wXlHjh+%pPB=7Lt-#x4CWy-wyd>}xUh^^Y&xUnQNk1KteLTy5ze;08w0;HoQcCw=oVU#j7 zH(`(Cw{(Z>Ppw1j!BQKD3bYGGt#}(qRqeGUJL}u}@JfSEff(&dg9v6EYtd8{TxBMO z(dYDFSH;xSdJibgpz%&p5SyyJv9FM2M2HX8zD8#EV*m_=Z9G;z7}GE!np41i@qi$! zp;Dy2)@-cfw<)|*A`?8#f>m}>ndgajc3?u`cf2e$15FoMabwR!#~B4J8?*5cRqYK~ z9qg8|kZbW)U%s!ttyYoCh5X!M3mJyCjLMbA8|umR$qf)GmfWR^?PNEk3{S-59tsCwhFH>dSX)ZI6!4pbetB*Bo(L%*TEY#l0h3<8y78v zq@D?-*2z`CJX7G=WSh9%m^l%(qT~jx#0L4bsYe{oFq{NWtL4-_4TqT0UuoB&QBTsc zx_a}DdWQ^7`a*&Ig&@{D?qnw^oV)RgmNI3@h?+}19eO7k7oI6;i-9NdDVie>4zH^c z@Qvv^Lg;^My-N-cLaOCv_7X`)0FnC{0dd|ynt;XGksQny;$l! zhCF;yGWtLwA~E`aGiM6lr%cPJ8zdvk`_(^~i`05}UWz)&dn7$lyN}vtB2PPc@eZk>2)b%s=>aXd^8#|pjZk{A zp~!bDqsI2ar%kcG)XZ;ff64cV9r{8Qi4(v>Y+*YKyS&t*<|n+=GWQWfj8|1!F0upc zQN#_4TE%s1>Xk@5wvITd<4QyL(!UD}EGA-`GF+o>!XC=%TV0dgc6?GBLLWpsl+Y8* zs;hUXm&jXRO+w_QU=?5PJHTZ)8!BOPK&qH93wo^YOG5)^ybK&a6ooQ6!bqc=duar0 z$75DQa%R{UkT7UzqzwdG!2PBNeGDM!$&M0!?-_Dvph2#!0JLOH$JZYcrgw+IW7dE) zNo0lWX{UkMHcN%4@vnc<>0xaKVu9km0;{(Y+fe5}&}sGvQalqL{g3UUhr5VFlSVgD zQy^%o)p;Sgg@^3cdII^do2p@@V=^2+@b>v4bEOig6!~v`#0BDJ(gT_ekE;zk zU%=qghX*onx9^Q?*WwONXMz}RtOun*_hLBc*sjp?yrtgo<9JZf(=$BTTIbnswHtHV zR6_?adHZFR#_42H)bGxtTkLnuW6(s0jV|71xdfe~+*G=w@-9a4u#|@956M zqX8&SX!5DLyxp1UGj|?ma}^GoVw zeli|epZ;y2YZ_M-Q=3+NH`7B0rvH@N59Pi|S>cFGeLl5N_U-(&X?3L&053_n#nKzb z?OJWS8g{_$G^QpzL4PpkfSDGg_|+pip>$PMb%>}PjtOF&^=F)xOsHcb_T&|>%{%ta zh0~i4XzrLqIu^>Ld!v3yt17wADvDnc70djtfdDl98%RSV=iAeZ89{bgW1oddd08D9 zLlsQwde}}qJxnX2asK?c!((`sO-j58>huIfL}{e(bI=>BBw4)QmAo{Q*>3vid%rIk zzIE;zLS9R9a^ie88cQ#*`kq28iP$sI-qFHd$*VRO#3Jgp12eMKer7~t?1vEO_$}8H z4Y-2Hfn%Zp9Ra<8H`f7-OjNCAI1D+BvvomKS*ur{wEN5;`4|Zk+O>A$-4z2nBuN;$ zx!B#Ma-J~E@;FQ-2Yd)D#gP*=!=uYj|zu0GP0yw z_L$|bNA|)^gZKqQx1ygdz%b`yU*RbY=~{Y2$sIQhg0M*Q;%M9?7LpzHHSO^uS$E1e z!mD@&%(WLthFm?q)F!c5`4p&6^RDadJ?SzF651KDWCPoThVmy9CmEh?uD!DhHgDCv z{0#Bmict$T#}t40l+>$DJJti{44kJf+rd#HZ1&g$x12&kwQ&I7g>T~VpQ+YOA zCCno`9BXg_SPLoOjQ4ZU_mU~-XC$eh8PGR}6!c8V^KHCg(P4a44}u5FjaP;w5~nIq zH*cSuC9-5`Pi_Gwf3F5Kw=INr==1+#*MVcT12koK^j36W$C$Amsjc>xv(DNQ^;uH! z$Eu9rDpDz6H|^%|T});O8wy^s(&vA;rR_kUFhjHCf?J}ZQ4l(=Fg|^(9v-!pzYi@U zkH_*^pLm8$oTIx^*=0zvPa6mK0$4?#klKw1`&O$`izlW|aLb%P#i^Cu3Y1P;rx$!Q4%4{mFOf^1(x0ONai+p4*{;HjbJeW4r9k#C z#2JD)id(4)_`If5qj33+c$$b35x_eMDbkrMy#x0`e$#nGSbhy89La}$5>Dke#xB;k&gmknv%EmjW}fm_A*=N|e83@u~B***2rG~uH4+j_MM za}}FP{242*-_$7^qkN-_94UI$Ix5{aU{&c1WOQLwnOIbSQwSxqA+-6?2R>R8#P~GN z8oMh2(QQW?t~1I{F9+DpoF0H_QE91f$ozHi3+2`X>Lze$t_~ADt~fCU{zpM$f4<|S%vEHvEoTb4AQ z0BGi)01~Lfe~7#ee|!VI+hwAlM|d{%4nnWK?;lLab(NV4H}RCou^}LKHJ6Ha?K>fg zcacm34HQMX5J^$+V(oTeu9>Ru6!tbJ7~eJ#fXT7%(`GV|?(%Bz>n((EY$A>O%}+D7 zo^gVok3n>Vz1cTMtMp#dz$F8wc3K?0&HbF5Ef4uTGB%CXot1q9FOJM7Q;>q%&6s!B zr1KWhw&cE1Wawq3yOik2OJI$KUgDo2XGX(iF-+x|N$^b_LW-b%43L{Is^M}b;hVx2 zyaa}W9EUpPZy%WORal-$E=6H5B@3) zh_iIo8=K)fJ-NFNcXSj`!!Mh9mpqSTGJEMRm3fXhFfJslQOR?qh*SfK(~x`kJ~7iY zR|dr)NtOQ=rx7gvs<%<e(C8y@+re(=JLaJ_-0F)ioEEemP9F?M7qO$I!Rm75g5I zgBdnu_}%dP!s=AG85+D9aau)WNm7=K)N{f}slSPHh4fQRY|#VA zKmYsvHP`6oSR^t%bwEUF<`H4*Gkd^+u@5MdpaiutK(y=I}}oe{`0vW#@up zP;ydoFgDa^>JOJ}j2|dC<+jxCCwZX>#0bQi3&*B08UlCg_QS5*>5(?L|LRE6ZO>}J zd8M`4G_<+LrBTCQXxpwZL_XG!Vz-n${F2*jjR26A+zz&7;eVpeHSgMz+a+UF-3kMO36C6%MXauO!O( zYR+-!Pn$I3vO5{hcCK4_;{2TkpU=GL7&bmzZACL?oh^P)fWTO6+P&%b`>^WtANjC) zH-J;}&Cy%F7;1w?>V+k6Wc(;OZU<-tx2B1fS4tW%5SbgYwoY*hkJgMJNqie=M% zaaj0KWp0;9wp+FE@2{F355LK_QBBvT>}NZX@r)Oa6BDPCH_6xSchgNP2ZfJ=`3mO-svH7>Cq0wF=cj_Bw63FdQ(Lx8Od6~plmeZa85=6SMZmm;~bF& zcbK@ig?KynuT~k-d>w69%_L)KTSrpP@^zjOg|5~E!iSeEa-P-+g{p_yx?9QxuT4>N zwJB1AR)yHKObV*lG-4>M5&0@vImx`w*YU&BpBQ3O1X-vxvQh-8b22C7rv9tEsj?tP zMNY@6zh`ED^TAvo~aj`1FTeXAp&0(vtUL3^!Q9$3{s0j_q6{2Do^`&K%2&x(sO#%`~7l zpFsP`s{N{Z8z->XJ-#`%+)Y$Z-Tz(TG_0U*;(>74(A%CE_5Ba$6fCm66X_%nwf_S4 zrXOj+^0dX=-oc1txlT;H%yblym!HxfdZ$)B2E!d{pZ5dWp)?OWaw-;z2T4*E8|-P>L+x9=M!o-o8T4U4#31ID})xQ`2RUyvKWOc_w!cgPCt z8y25rI^nU3=>NWnpc^es@<*|(a2;#rDEN+puX27;`c-4BL$$HK&44lyj&qF}#r%Oy zcwk;Ts{D0szEa9Sut73`#3GC@ZeHs(LP~LW>mq&)h1W|)kb}w3F-`ZC4GnR8!kEJY zKoS$xQJ&s1V_bkL#BDmE-H)=)$%o|IVsW6odz}6gqy09*o2!kj^V&!Hrmkg6#7%$j zfn?GRIxd(%e*#e-5wudZX2_wRv|pJJ;LR<_&b!q@K=#Ff(Yd6}ba+yYpjF<2rX@ZH za*LNE_jJ&u6};O>oAZ#uXH_2alU5`vopjnW5c^-)`K%?-$aQQ^QG={EPNi zKP6Q-f7!3A{uZK<*^M-x8LZ-HL67W=%6bBT_N9{L4GdlWvvE5Mx^12I=p|`46yx|2 z%Yf-Bve{QTSxAh}JtT9Rx?&0uqQre52(*@f#4w#RjgNoPTV%1^b&=zgl7ACr5!QBi zqE+(*H@MjspG~yH@M#WWiD%*>Yqy|{%T6n3kMC$JFJ|j%_C;|Sa%q;3|i z`oJ>MLEYpBjKt&jJ~~$4s2dz`8>sdM9vTICmEY*;#XOzAvIg_#sJL?t}}j+HH=!IHUsTPz8KfR-CA-S7P8b&E@i70^CIWkcpj#(87&TJ8%v z{i>0{6N@${Y2@CrCXXv`tFB2dH68D#O*YfX{T`G9QPk0Crn)i_B>>@H^o$&_t;z76 zP{hAv$Fw7Z%~&$6ZGye(DXfD_09DT;mEPR-I?2%tbE<@mh*`Tb#s+Pw22sE|x3`(l zB{VbWp}y$a8h2jIUMp>5P$+*u-sPi$E*Ez6mEV1~3jAwMcc6L-gjKG|Zu+!JXJ0{* zazhOsZSY(NY20iZa1@X<1UM|F;bK!TxzVng$(?e*N5=a#L+I5px$b(pwGFqs*P)j6 z)GYY(BU#Q%QpQY}WjLYBU ztnxWQ=0m2YwPYcI@coLs{LE43r1Y}Fnm_Olxy!OjKGYyBuxA&5wRW5D zJ9|vR&n!$}9H7+rS~Y>ITau8xD0+-Et1WFSt57=HaVh6E6o4tEZ5O!U?EE|Gy;dsS*x8n7y9O3rK~X}`F1s**-eRvf+C7cz4B*%~5h6&5r+-k@ zP-vxhFf3ivF;UV?-S#1t=+>clLZ=_x>Tn%D$qTU8!QT)8@PEB1?d;Q_ynJf;%Kv*7 z2H=k(U-mE*c)0wwRYv9q)NcuD$JW__awfE%A0Cf_S z^yx0mCo0$*Y_2CbKb{DS{dr=yOn>3W)cVhkkuiVok^Trc#WvfKubFqO;@9Z}*q@>5 zgCwi&vm|jp69;}y=S_0qH)4Zn{PcnNY}eRsUlh+3VRaKF$~nH~Iqh z3&+@&VhV6H#wdY{TYH`GmjQk1VzOjE&Y7=+`qTFc@Ul>*y<06i(L6+4LPN;K5-?G* zV03gh;}93LtWRG;+rQWNw^%*5O8$Zy06ij*!wu-e>R4;6(~%mkzx~g6HZwEscH+q9 z?w;dc&HLpg`1t-@KOm!|<_?H~kXx+eX+#-OGH+hK_sP1HZXrd!n1zI~B8R&?Fx{>6o^o%GpfcQwfB zuoK&QbF!L|OJIi8eRF&|7v5Y^)1mEqBG_8f7Gnw*hYq=qo|^S1Ch3QeByus$6!*2f zkb&yEwG59&=lINLwF}HvwF`R40Emi2rgN~}wP2w=XafS=z%&SN+Eh-w8;ulD?zI|j zUtnylC~@lqL#I@0^5wO)`%BpU4%U7?sJ!2lTT)}R5XuK9YN~Qn8U}C}LEsGX=r&ex ze>mA*;f#$vf4eG2xGw8H+Bf_w|E9DG&)4i}KEa=_0O}Y;Fsh6?z!T?cxSO9&tN%4{ zgli-dS6uoB*Ef0qh4SmYB;T^8B<~uuEOP$+Dw1t^?WAdtY`XUe(DzAGVBNZk{Mk~c zh{u#`JMvt-nlq)OIrTl6B@;p4FS&7$Y`-j-h4#Vq`C(MdSkb!Qai~m+C-RF7=g4z2 z@fzMF=(?z#A^lBnB?VE>-J*4>z;BJMm|8SojPd4pAk{LRd1#&(o6J5?a-XwLmcuI= zA%mX(gIXnpB&AdM0a1IRh%{DY(I&q%Kh7W7QTXKpPUVoPf{O>%J#W&_{hv0d5I%=eYmry+7UtfsFY|7ENAQAPR34*$eka z`ge$^8^vL7ofkG_7sBh_^bR-MHZK4f3zbxN11<^|5q^%h#J=L+4tN6K9= zr-RQ`uh<;Bo$o~q10dW@<;aEx;GQ!FAtu-3Kl&mZnAR=PdOGk4fOjgHM6X9THlJ2L;3&m&s@g8z&efJo zzS4m}B??t5C3$opAU3|um;lptw$45iS7Txi0S%EC^mNYgst3J?v`d2-0#n5>w9W`d2b>_ZA>H&kLrtjc(qSk1b zsO(uUyeT!jQPBXYAn0(a>m0ClGm)>SaeuzdVGSNPHTrtD{~=iW+VFoX>l2frkeNH2 zLzH1h)K$%(=3QBE-6=1_Cb1S%(I}`A3~Ue#BA^)eLSd8f@MqEQkaw#)4VO7-{R#44 zVEqC+93@je*IAe!lrq>aXPt=pjcr-SUpt>sa|Dk>Yj(ZoxA!cJR`$1U>mf zleqjHH0v>1dy-qI9JkMo<)eByhvUPZcZBs4^d@zS!kvX(E-P5pt9$l}&2wfu3@s)` z@%+(~KccEfOk;G*85oCl$=)v163Lq9m#waa7bXVY4Y-P-Q<|vm8xM!>RaGh?Eu@UA z3u!&`NINrn#N|D=l}{IaLonI=;Gys)n;joDcxIf4%X4(-7SnujVd&?p3yP%SP{p20 zF)i-c@Y`FeK6Uce&$)_}PdfO!pSkezFpcvE;pOs+;f~B<=O5?I$s>q7%z8 zT+sLO{qy6z3@0F--`0^P46A6?dCylEW1vwWdWRoppmAOxL1kDj)4=>!s~sg6d)o z&tRNx#wIU~h1n=XR7a9YTF&R!SBn!lx3Sa7%RMlhuY?B!4d%(F6)hp-!ooI-!4b>Xr~dCZCHl_C-HY6X_ z?v!QhN>fKY;bZ!lOmP?T)z*r;&`Sa`eczubDG@VwrIFJ&K zPTZ3mjt&~ISfC`n6YDPahaAbmoAH}$vA_bCBt=xO@rU0ByN~aw zJ&E$U+W*5A0y;0X#33IrWVZKz6v!S&hU4WXf3M56(*tRSM9bNP?pb%6709D-vzr?a zK1flHnTtGpJL>umzz=eeH?z*$oHz0LV!i%hi^#*Ehjv}n#zsDZ)t0_J4~^8G%ws7L zVN(qzujPJ|bv$YoUEve0%=30slW9g9t0L#4i=fL#Lwe4gFyWlL>Z^@NP>8DTel=NK zDa+e5%RD<`8%Rx(fthSqHBdMaxwwwIiWiM!=Qhob$hxYyW6?-z5dQ(t#xTEfw8z%z zKqltj7UlO7MiSyu-@Z;D5&T~5TQ>`TtW(cAqh$J6TKG^MnPTN!(;6Bp1vGoLEXats z8@SB%QkYhuV4{rYy0@SV+d@6J!j0o&YjR6#M+?hdB-;BJ9G>Y`d6&=Xe{u9W@Zq2K z33kn%tS<>#38MRzra4Vtah&J5_;ew4SIJjyrq6Z4n(K+XTUnXY_lEtJz_{v=$*tiFz-S;{>4#V6=QC89Oastq7b0taqifSH9ysGN$8inW}3o>91k#&ageZ zm|0zb-XaNuq=Jw6Ddi^;1(r`ql#2~Aok?9p12s&jbAJ|yi<6KUffPYMnaQ)$i{ZK4 z3F7ue&ctweqv>HYxDO&}^57G+zasQ_8{na(YLZ1~LDFNjN zP1OJQ5GtBd>RFXrRO zafbwWfxpNTSfp>6&7)p4xI;S*iGKn)XFKR1mZ2EV_5~UpeU*(&9E=#U;wL~v)bE`L zd;aqb)h#BUes2+xK|T6!Wuy>i;CL$@QayfD2E$_=?XFDuv-TjBZPh>s!|Dc>Lc5%&10 zBy*uZSdVzJ@+=T4LU1TXU9NW%SN7zg`w`p^Hhai15Z(|1z1@S~-oQ(KkTt{C*9s6` z4&>Ud>v89>d-d(0!tlBP=U<<}s_}!WN0nW;Q>Vq$U76t0%ZJf#UFaOp@qXX*cyQrL zmeaLga%T#Re_$eS6Px{t(=6TB<9v(Fv#`WbF&pn z4)#;Q>I#EPxmpUeuIhLFafY+}=FShJ%@w#`Rbwp-nA+!wMnN*yQnv6_*A(~`aZf7g zlwJ{2q;nNf)uwv$H2r&Zm6pNH2*!@$^RM8uvDNjF9GVGpK7H`p_6Dd^Xjkm{(H^pW z93YVIIakpx4B8aRb~1Un5^z1AhK$Nx@I=DwJt?^8lOI}^e`)Lrzh>r~PI(;7(tZJV zFx!hV)=$2scqYXN6`D5FkO~$ynJZOtg_{(j;;1z|_K!T!C7%9oo9GE`-$SIPsLw@O zDP&3;W#b`}2Qt$^9*)=Y6=)x~rErt~AeMLmXw~);=u&z(reX-I>e9Sw5dy9-8tkxI zxeBxD;>4GDSZWs!v8@}d%=03x-DUJiv4hGbr)h6&pS}6h+|J6k(^G&OD?@_R4;0CP zjT)}WKd7+1n465o!AL*>rf(o%fES;M$!P4|M-Rz{zMK3xaQ9$sA&p&!Fu%p7rCNOnYp&Z$`NSIjvmc6ZfKbMPK{J%DG@Q1lF?%DS^D>D^L*jCqFN`JD_mJeTx zMwaPAM##klsh`Ws2^d7zSd=Pl2!u{bz*Af0@Dh*MfGQHjc52ZQ33)tbIC3fc2yL zF6cnZ7n{}UIF#R!weKJh7XKI8A-lA!MAa&76-EgzG8t*R8ZXO~@%E2WFPV&KImmZ$ zxay$mSX11;z=>wMws1{#avfA5Ion%J_|=Uj{j6KpQR<@OT{z$EqZj*()j~DZNp--K z<7F>C;Zq)##Gl-1LtNiQl##;o^V+*@<5SWM5jkon)Ssk|T5-=#O;PdE11)!ie?4|J z+MAe#FCV1ZaLy}N%LbP?`jo=G=@wBJPiIYAmYC^OFstECJ6~QM5eX209QT{{e3Fgw zcZ|q4DELg+UCK{Hp|cy9Y;{&dJM+jk;+Vl^JiqYP8*Rjisp_n~U6HqwpENsL*Kbo%h8iNpNK%yyNZ6!6s zvq^H~=_ZH2@~V4OF$J@VuPt$6W8fE1U)J;-Luky;nQo<9p!$n9ioWbncG!)Z&_N&& z`+#5fA*}_v`(;o_8QU{Imu74h{-BW}r+VS$2nP2}^S~#zQn)-&`T6}F^nWN=8c4d* z^IoN5uymUt}9ZF`qp);l3=?5W}l3xxnycG*m>RyTkw-a<8O-8zIj3Yu) z{JM@nAPzYrW-p#YX^!s8^AN*KLtZzB4%-x_b)cQK*r1;W%3tmq1HhAZiS$GWu^%{>`J=^$haF-t}1zC}KDM;d~CkakYSP-df9*KNMTa zFdP>@0Ih_rpXKDMD{RSpuAlR1E(2|)d=j#(L;@)~5QV@S2U|1aXE6SnKY>=hJmb3yHnVTk5X4z4BghJQLfu=IL zV&kkRWwK9bNhyf<@kK|jH0{huOBDy7la`KiEB=zF1C~%-{-EJn(zl3~vU;PT8Doc6 zeIgl7&`((=5{Dni-jYFtp5y^6j>QaCUg8UJS3FCYPD#b3)jNE+8R*rD)$$r2M8E>S zQ3Z(jQU;O;5Th{n2d|>VLk>9l7kIr5&U>9`U5&c;CLx+C+r@itb zyfX*at6Q`YgE1i{SCV4@UN8pZu{PU&n_QG$MFrOLLv>??1&nxH)t*0UrfG5Ki73RrUYRD40=+Ul~3{2G4_LX1bS`eQsC zL?N|WY%>v^(ny#Gb<^5_7Z~%o`sx<@Y(Z8e4fAWP|E}?01nn~<8U^!7S=i(+aR~^S zANYtcPnUrDusVJoAhUMAsQ zo@DU^CG5X2Jd z=sL~=m-%-h2`6xQp=+7Yki(|@NpBY0l$viy1pa{{nNmgQR!#LXiH+jc;gyO>w zBa00bqZve)-+DywN0|Q!>tCsZ;(K-JEbF6fDtK}#eswT+Qt^6(g^>pIIqZI4y2$o@ z6@ERt5Qd!iJx@;C`93XF`?=g?KrZJD-#~{vkmsB(FZbsw@cy_Tc22kVQ+>6sMmIg8 z&(rmx;e0hWywA(+?Ugi(p)cg^Vb4pHlytM}^<+H7@cs1$)U0%|Df4_iH@EwCN^^a1 ztl;z7{c*=?x0_x`ugl=Q=-Ekh?=2@n?y?DNuhzYt6aL_*tJutEb9EyK@^QB`08Vyy zt#`l8&3$;bZL@tJTm1Z<7E`K)eNHmpce^`1-286H?7ATL@5iL4q`P$=LiU!CBLrCQZhbocxI6dS_8Yq_o*249ZX3Mrz}t5hZV!Wv_j!*?WSUgnYu&i;Lesm` zkl`H2(==rBV`YIHPeTq3-^X(xFNW`P3U^D8*A&Qe3Z&8f7}E7L4pHg2&Z$^>A2)n& zw(JEo$!Rh#-SBe@dpT$)r)+UCLhl28rwW#1J$b5wsMU=_uGuCUzDcD_FY@gB49U8@ z+uXfi!=rNlB26N%kLx*1eMs;Rge4axM|?k6;_-X;gS>7+Uj5$oyWgK6AKzxsPrkX9 zrJ}P$WCXPMuHPIr7hta=pc%h;U7}FExNlC<4xOI7)Mrz7vXk8p}EyWlK*mtI4qjD7 zEippn(QF<%=^Z8)A1i^e{P@Z8 zlq&jacW;1*mWR|Q>L2Tj&vcKmz8l0V-RS++eUN^)Ui#p;xS#>%c;+En#xM=U4k*Y1rG)dlKI zI+f}FPSVi+syJ7hb81`G`^*MSKhBYyK(wB9UjBKJf`uDAjwwsC1qYjK##sDK#;vX* zStDfLU!Xz(>WBz_1Q@rn!ePI-myA^~YY7%H8|9S=LY%iVUvzbHr+a#x&iYT^j2)nMMmeA_Kar^+OIdHg11W2v|9u30 zKy|Cas`Ol_{T;#4sW$(mB1f~xMWAj^gkaSa&;sWrK!p(ULdSJCcFL0XLYMN^8SZsw zlyX5pfp>MllgoTE7->^#7Nj@6{I!t*vqMrg+hXcODBhw1FS|5KZ!-zit6+~gJ3gz1 zX}T1S#Ckl0ZJdU53EOqQk{JZU*;7Rvh^D)_INSq!f1QmXf|!~}DPknxdF=MkOtXhs zeF7UZRwTT>iCR|lA7{WlI}T|hn2WF@+W>(QqaqE~U$_6e%Gmuw<+(5T=L6Zh&6Sxi zWZONAXi$9uEFs=DzPGsY)}1jnqJl;7FlwlA)L%|uRCBC!_xt91cBNgRaX(BoqcoQ5 zu36^kV=+^evXe7&(|VRLv&Qr{(=IKRHNtF%rrB{9E+S>UrfiuB?-he4ZN6RWf)u7E zslfpI^NM^dV=L!;@k4oCO6`|GYm2~Qmml#z2CW@-4IFnJ9Xj~ho4;g^_Pr3YPWmwQcW1lnZNU!NZSo{SPsamGpQ4hb2W|8O|FNVoA)%d{F_ zqtvUS3{h;=tL_pe?EKl>F5DL^;CPhy<>7>=aP|uxQK8ZBQAZjf8~1lji+SJos5NYO zjL1v=#y|&g;(i~m-}Cm&tM8$$Ov9wxmLz=6V>q1MPo-Y}_lBM(r9r2-?FEjaW9w6U z?UW>LZfcs@2Sb>u(kSdjBMvw2>&?4vm^n<3rPHClDVr$g(e{nm0Ozp<{`RY*El&^_ zjIeZm{E@Kc`n71(D&4c_aFC!X-&u5(%J=ivmnLRh3v9N(vndRHCE)c3FbP8#}!@ z2^7m6OMOZbIic?%z07j{VhSua%`OH=1cFw(VaW^|DWdO zBD(luFu6m3kS^D7={FK%uH6b3qp)?Q>{BmDQx={6H2z7C%}7H3x#^J-&$;IHT(NYA zb)}&ZJsDqr5$=4WCm2Ui5EGM5hFsFj^*psrQe59)NUUllZ=c-I<{yUBDqHslKWd)y5Bz}|-`MsHDb85uugy&Y2i1&7an>zjh8o|+I zLuIMiy|e~v^DF$xpt2@C6nSo$Af-Q+c{nZ%ZiU0Re3Aw`fSdfk7@>JQE)?Nq*av!) zSMn-UR%+EdV6NjtRz%4ScE|0UMsbF&Yb%gfvfD>$UIP|}x&^jf#u@mSMywRB!|NV1 z{kuMpUhmuTvs+1rYaErPR=jihNOq_w)vTSTOIELQ$C*lxv6D}F)twbE)Py?Bm&Q7 zQVeM}2?|Qp)36Q8p!G;cS%0i7U+&wd&#)k|Pa=esJRWYw3&5qnMU~vFUhe^`Zpi zBFjrdPl5>+3Pja=2@r5PGV|r397PTHFNfC-Dv~<&2 z@{R3a6V0l;wh8~{@{fJe6=8la6S}6Ije;6gmnTAvJcv9x9fB5?ny6ejbN= zyonq-m4sv7?`zUbNd8gE7IsJ=&hjch1AV&AHY3g}Oo z5@Schcgax?qU1VdlcR%axeqQ?`xriQS{$j6V%7j#zyKFR^J%m$aivpz_J|YU_+zks zCW)*?giW*teN{HrzAo)rCaR0O=Vhrq1*^;FBso6O`CPSRWRYzryva1DDZZ@Fgr~aca!jmw`F?eIhdEqURYS3a z&q3yx!>?26V#@jV2lDbC)Ho+gKO6DdiXOMpGrW^<6n(8&weSWRG}vMre@y0joPBrb z38Evfj!4_hSl`EYG`knHi>*I-3uiVlipP4zJ|UdIyd0)j5pK@FrxUk9wO1^_`d8mO zTw-e}et4T*KD8Zp3p-SsaBA}6(w8?iY6$*2@Jnn@U#jss|Mo9gP=)!fiyot4GdwYb zU=NS4{Csas4|X~m4ojpC!2&?&R?+x05@<8O5dqeDdMgjL14|M{Qr0p$9!V6xij$BW zy}a?~C#wx_DB2{gus(;@9R{RW_P|aP;c;l86Iu;zKZIV!@FeVlt-g+pr4tZ&_(zg` zBk6R!N9Tf>>U697(NoKj+a-5rl8%L`aO{)~EKi@_MVR$W1ZX+>-?vyv$&!-SokW!J z3Ams4aVg7t8qf<9d?$KriJ106Qrm7wJ1&|1dSWG|!E0-e3vF@tcZc`I+wplKwLadX zd`V()iZVC)#o#yY4I21OLQf)kS?szcZQ!q{7flk^N#=?T!4gWP%+$JSWm@h)xE$-7=1ZMw7)No9Bv<*m z*6hHnh4?EU*bj|tguRhbY6yY)jRDIKS4np zGrZPc`zZ|3vMMxt|KF-jDUP_T3l>KlgV7_X!jBfq+-} z5`h`ELkw1zS;e+7mV=W4P6RGy?INc1+eBjNZU(~{q`wLargdjvm0AcBT^EH7VS7E9 z2)!2|?8=y7V;9vLh!mXL5=x?^CK$N)k!&OCH?SL3Ah23Q9k(P%8Xf9|kF8%v+J@Lz z7XrM!1L1A&NkI53Rli*;Kgf@_piC zOspIB(xnv4$Fg_L-0SRF=Oey4YAt^4xa-^Z$Z2;()|fd8L=_KQ7tN>HD$Y9e6Sw5Q zPkaV`Q5-hNq@0xzwk_Bp&QXlyY0UhhT22!tE!+F(tn4vdPE^^b74PWamS#965*Fq5 zq5h*|z*6bY5t#(0JaF`ljIab%A~p6M$sESzkD5d*vy?MRdBX42f}__YS(R3A`5!&S zcJlGPS&gW_%f7ujWh0}Fbk@)`C>q8XPNlG~eO858K#4?bMhU5EHPrex3qjb6=tt#lpLRe+$dUSw;%o&k_jYxNgJ~n&& zdnL|P&9IHawlYk~5d`O*@tT3U1lHQ&J1J+_Qb0r#nI+-gJlxD}K1VT0~3bF|~qY{P!-M+izpt2~066ujc%?+VnZhYzu&IVuZ7mg+BC9&>2l_Nwj z?vAJ%g~4J$=(-l*C3TM@>p_BvXXB{72YA>>n*j&;rX8O+uiopIns2Rm)K)w>N9cH% zGWh}-@Q(Mj@e94%o9JAG5RWqi04a4&OY@yWgn)fmH8|gW;%r(nw|PAXE^>aVa*HuP zN?vfKCiRw9o{g%IRM)8^IIXc$r2qt>9XIfPID{z%JMIMSxOtt^*nH=}>gNSmELaL` zc3Lvm5EQ5DBx_`YL&+6OzCo7ZLCzHf-xwlR;U)SdTZ5asOt4CRdPl_PQ|-j| zwB(HObTqGo#1!rxuMwAjt*d>)^obIxAM;YA&5j9B(C8nnFB zUn}I4*>_cjkIEdp84drXD)Qj}>rg3nWU1@JsBv{yZ^ zI^$yE;$r?@NUt3HIAK&Kn>PKRzRG(5w^QF#h4PcT`{xYr)%6s^%f+dS<>^mh>y9xP zreDn#vb6k%y@L_?p_6xI_*EdE0GF2LtE^hiV?n9YbBWIa5)ZCAMdGFli-r%zy;E5f?POuFgy{UrfqQ!V;B?G&?`TQNy=Ps*k&1nzwG&2UN-=ER7^4u>FQk$~80~rrG$l z51ghSLeG0#Hd$v#P|oHBpeViqn_>Hh7cY?!~9} zTk{{1R(_3DR zdPYlS0;=kYW(L)%FumXe;}hyM!;V>ZF&66`&-C4!qpB1RQ@=&9p=9Q~+Tl zha%Z*hX>~_ldA3CRy;u2+^u6jPFlK>*OU-?sUI!uD2#9I0-R~Cs&@Ew3Y(ztQcqidad^hwHbnBkeEybySdHO4Fu&z1kcWlyT5R*%2p zqPuH=j4v}(<&f|l%-Y3qpmG4SjRs(Du>8U(0x$hesK;nLMsd|la->5Ul)oVVy%wL#Bgt4$S3SDK8?6hBY)2Q@&&%Y!c&& zC(_4%j?H`^AYXwSn0MOavl2KZ?sUs@TlP(m_M9=h&Y6sWDml%S$6f2AzZTwq`<@sK z#A>jf;7N*h7N+UA|8x8L7yOPWUD?t~5Vk^LX>`daZ+%JW)?D&{Gr(Avyg-HUf-tMm zjxEVif*0ve{bM)s!uKT;4|c{9C$3qmO)ChtRxR)Ec;(jf9v0*Qpbj#s4$7!rJB4M~ zO%Ssmh)>e!T>f<+W;OFO8>oA)P$7uuLqF@JcA0**%g}&K$n3yH2s=FMEqfQKTowuC zK`jPOYB@Wd!o{(HnM8Q8MBy-@!5)58jbFrBkfm3!f%JaX3Ml36Mh6O9#@|{zYV|r% zncr^zXMLJZCbsWNcEQXjP_-kDLQM_doCRzRVaecPI*I*h^o(&d=17Tz4Fs}WRkfhT zcR;nmeSvAKhN@^Wy_j!0WL7+2-^X^v={QoEd=c{&f6!y zbrcgZ8~~A#i(FleP~i3)aJ-<#rd_%W^8T<*nCpOPPX8_*p{t=0eNy?E&ag&MTJ!F) zZ*1jvXRY0OGx@CVVty*#&%!V2&6r8Yzhvx<6vtkUJUZ ztVfNj2>sx%B+LJZ0n?>WJD{}O@!`f2Ty;=c8eX3>_~9ay_vKrUe}qdz;R;?5B_5;>XVpX>6SjlzRZ{W!8}=VolxN5_2n48LxFY1MVDx7%se zvVsw+pLKb{KwsJtf?vh@^4)?Pfdayw$*lNUUT3+dMoBk3c}z)a)$iiNMVfyR`%OZP{R&Kn9dzRXjOef<4YI zY|IjK04n(-1ymT-kz{~J`F3gvc{LpA0Va1AinB$~rCKWnyQO!X6h0sxd>tH?W8C*vs@)RvJ`*7e~p@JURlFCUmGZ3CE%< zLLC-w9FxhxeXH)cq{o|<8ZaRDX&UjcQ!fVC*erbDC(mfA$}JCC zI3sD2C4_Ha?21WSSZK+Q}&E3D{(oA@>uC|(=Ho{tg29o>~8pEQfb@W5keS3Hu=pL0O zczE&NPGA@Wm>Mm3mQpLGN6Tk^^9?8Cf(*qBp&-k*pfeV>ZF#)%5as(J#{4)u9i z=AeOsw4^MO0^xm%e})MJg#5+g5NtDY4X8a~Wc7g{Rn1De!1)R>F-KneLuWyze_q*S zDpCG*xJIt?#-%07cBiAk8@J#4SC|VH4Bkki@{sSrg4Ws5)cvRh%aZU`TcZ#U)Qo2wfr;(3wfBtrs%OoV&;;h%BrPq0pnpF@V0QHLn$4Kc2eElgdeCghJsyV zshSbzT-!SCBpva&D|u)5>aG%g!0raS+^}<{LT-W36YTme4#Xg*-e<*#UN?*;`3*m# z9oR-}f<~(dZNB4=G2y zX9YQq@HhiqnvTiE`fNX@M8C)}=5@uN)Gd7UDCo-9Y^UnNg4q(thLyoJv@Eg6;Mi@j zo*-0}k&TclWlSIRV+F4-EAjo$qe~id?$HOP`wUG1qobo zneF$1Rw8_L84Wp0ASW{W09K*`S3?VFYbiGyi^XFDp+rEo5LV!JkoQ`>zG|iTyrHMv zy9WD|?T4yVYS6x%hAKjIEa8fRz9PhSgB4Qhl#k_ z%0RtNPTADV4>6;3`laB_MXIk%_3$9e;rWjuymqM%+A-&-$rwnS1fbN%D9Je zweE~rCLo4TlTH)od-PsJE7aJ!1;0urzww3#j;ISY4}JviD9{ZQ5DPny>JMTVHRn{$ z@=(O=bJh;cTqjg){-xs)A0la6r4QfI3_RiiTm(w@2>ol}1MzsJ8cS(o=2>t;Jn%EM zfLPO>j#NBmbpX3Q1vjWQ-y=?1x}Wk*-A7r?p{$<2M4`UW1}QXtV;4Em0ms0|8`FGB zwXx$CLp_U?XN_f;G=P&sWIR#}0G zh}Td;$4l!$EIq@a5?Y(CeiD_WtLz6Z8+!Pf=qJIBjQen{2JH0&M#vEEGqMAXkOE5< zOc!&-J6U`aoq{wxV0KP`;z0z;dy`45R#oZqyG4B${xaa?Af8(~wZ1!gAv0;#lKbRd zpKcMG8ik_RbQ&CP5OlbuE!h#8J!1s6KVc8S_3RUuj3yk#%X}$za#L=G{?l=P$NlpV z#NU3(_m1@FaU^$OCSOT6sJotX_Z`@?siMi|qZfFlRLi&Lwp{05oSqdLKgTLAilsIG z*tc36>LgQ?(V^*Y+D~`r7vx(L5huf36bTF6UfgX zbg_%8X8rX-p`^kGUyrsOT48aLKV)mIomOi4ehUBU)Z~ugbnQ`*@V{S!Ty+$JFlG51 zhP2MtcObHF!!8|A)zDNxRo5V@+Iv@!19VOxH(R;DEoSccPKJWrx*iUL@i;yg={|I| zH~O=7SrNRf+wQ(f^>@HE1#6YJM|PdquA~%hC>8SwP93N{d*=^Xu*#9@3mWVRW@QG%W!)0JS0#otz>7LAId9=mhjsE3p=lM=;wRti?G* zF`u80GI*A4t1Wc0v}%kO z1N#~jA>f-h{@1>?{nt-Au&=odDpTwqi&mBESZ=j`IZC2rq$dqoPITvSksuSHDRWfe zE1dC7J9FeMyw)*PX)`GMB2&({s;KKRSZz=G&17zWfX`*DG@Nu?uz|SE?H~amTB@tN z^UL6h8m=vJSt>VQHXlXr*z_HdtNJa;dr&9m>+1sCNrBLk$!=-R8g}4WN}giW9aU#^ zN?_;{>7DOHTU9Wy3gr7Sg~3%5c^hFTA&SBd##o$wj`K^IYP8C(BZRND%05QCZfl$> z(s2+x9s1p$W?<7i<0d@%+6vCQvqepLTZ>rN70i7Eg(r;}_50F-^qOHR~m zYAk6ktGMv61O)d+TrrJ=TX0J#0Le1B==yl`Y&GBKUky2TG;UchqLODuU_`c;2cBqv zOr2p)`plM}>(~UFJXE-u^KclSV2^;W&3*4op;ls2Hrx^oDi94+Ae;;8;Y?tEg41X1 zl|iKXq+nvv$x|1|gPukuAo1Qmb7kqf_R}GKpGMp|ARp0yd^~Z)qpo0MK`GHM?IcXY zN1*FkC+DIY#I7$L?{_?O$|6r@Ff;FgEBM3+ z+Is4(ls5VFNv;%dm4?7d#6)3(0jOYFPe)MaUY`oLyrbt8i@JESX8WVZuX-JxuZU)> zm!;h4OZ`E1?t>vOlS#!CGk^Og#(r>c*{~JhvmU0HRVkbu;!Zih>H&b&!QDs71fHiT z(W-q5UgF29(pkdQPh-#IIYwN#l4ESedK4cKnZn!gUFM%rg7#6tqL7wY5mjE;tphAL z-Y?QV-|1~NFvvm!Zco(D_)#97B>6x~PVxkX4^u!Xl z&2X1wn|m*OIqs@!AX5s+Ka)_H7oG9RTbcZfOfGqF(8pLi@xZ;f3j|!%#Hz-_iHMte zC4Ae1p8Q6Nhiso7?l|QK>d0ZDg8PAm>Q|=qB(lcD_{>4iU8$4?y4qjBb9T)B=aFJs zW+JYVHD`Vi{CvW1&uSi;rt3)YNt(U)m&Kp+-5&9HT|7P=H$gQ|V6Osd@ED5&UOF&X z*TkywF>4;kiYfRAnomEw%%q|0;l7}T@ZlF-M;a$dsPwt;YZ--GaS>67Sh7Iv7u#`? z^jy&0)Cw_o%3n>sp1V_LB=CLS$IC-h-@g~O4}YdDtSUp`g^hsWuU=v&I_1GDUc8{S zET3#?HkilY8){C}+UnHF(y|+KH$0p=@RxjB)<#*r$dkaf6QqIsWJ{Jh<2;r7AMX8; zs3Wzv*ZLvM9PEhtxPw?8^>+Bx^cQz8bZ5{FoF#eDrM18V6`gVfPx<(h2V0zd6!Mvg z+z1L{4=dscu&c>Rw!pp9WTo2*c|Wh6FTL_IF03Efn)AX?Z2LcT?=to}4}(=h#i+pL1n|#5vn5F!G+Txank|hp;B6H+emnP1)<0F-+jKPV z#hcgfF(tWzm}#C;_Im!#tpXB95P?)KZGps*ixp_u4S-}Wg%i*~9JN9QjH^=1JVBqc zCNWE{ygFIt*^X@$;?%iS4D$BWCZw~z=36>tNb`4*(gd@iYQSkuahjj+T+0D{t%U@mA5Vl0KlYM0bn@M6=Qkyv>u2P`u#^LA=!ftc~b36HEsN=K*uOg>^kdoaBjY z+RHk2SBcVD_rY0H zp|chnrT;HN7?sc&xOh|d%-HcY{yLpAS|N+c{#;2DmRvn~lzdRu>Y7<FQ0a05tg{CyEs$efQF0R#nan~rl2cZg*Em9c(9t0-!Bi@J2DS1n z*->HoTOfgh%*4v2uP*9X5YW$-wiI32u_~EJcMs)nl^Hb&E@Vo9a+JZYblwD_Zn-VW zIC1{}$P9r;DGZ19p|g30lWl3`vu?wVu!XFWZ%VB?bkb~%&uM$`%mgk^HP=26i;{-g z@t>^bZ(xKIeBVSe{x~?AqX!k#=kx!Cg5E#C(b%|vqd_Ycy9LZuZ)XHa{uWunGZ1#- zG*yr-zzirRDs&xG2PyKF0O|r#0JN!q89omkPw>Mv zg4I_Y9lOwIxzy#bAYZ25B(4Imla!Xs+~wBFmY6H258z)Bz&|g5|Jn-^cMnVO0Df)6 zg{TMp*D0`!gTFW507tiU92#A^!Mvqz44!5REu*(DZ zH)IjOV}gS{!mFkC52}0|Hkw|}>HwL6HR&>V_g2F)ZcG<5zAgm+$l222v z8kj~oVWU7)t0Oz0ahh`U^m#Z$9>9Fvf8}DKOH6fq`4h_0yD582MUu9`9~IHQ*9_Y` zGS92AGBR%oa;^Cqmory@LWzaXpoMg_l)Ky{i3b)Z&?1D-@I{CaZIkVS8g%CuRSE8M z<6ZM_MI_IX1POO^y0TiYPont|lFa~GBN=QYo6{>3nUlb3D)4^b6SXRJ8jD@QUIdq^ zk9-8^RTH$@0y%e-qHFQWX?5UU@>W!?(Rsv$5so>M`>{tkr&P#jKyRZ*m6#Xi*8Gh9 zm>mwNZ4FvTORJ&7iSNomFHFC*1FTj9tv2VIQ%i}HBa2Q4W*M2$L_+Y0lX%%*R+|aX zt|xNPHHwxpZr?tbi*dmQK|QJ#p)6~zAwXMTz^=rEVb4QMtvNWNaPIXT zg6;!Ah8Qde3$ve^O`c|qee}^8y}raz86h{E)?7(pHAQLk`hF#X-%eXI>0+W2TN$bE zQ1x9q3u)}7nxGsfeLa4>JpMKV&lKps^rj9y`fDfnxlFyO7d7@me#V(Y8<6ZaU3wW0 zo}OaoaDR*_1$M#4o$MdsKBugCp3^IE_HNdWt8-}s@AojzXrN$zX{gbRdx)DK?i_fD zZeYG2RLap^PTJH5-4iPOK!WF_-(aSY{5D`5H};iz3R z7?G;FL!1FO>4pt}hEzznUy3aMgNdJbIHPvC(ttXaW#}RqTCpg6YXkBDb9yrd z+RgG>;ghynFPB2&^LZ@1VCx{K#)!LV12?TJ{bgQ;VE+I?bF#EY@-y7o<+*J{<27HXud@wO%OCsD+ z)Ym(lF$a(~l>{bC3V9+u@?kaQSiVmnRk~Pqdjf|qxPPs;xyZl1=Zw~4gFEd7YIbEPmsuy-pN}?#ozdxf1N8l zsj&1vQZh*CzIzdi&%-Lk;v7K;>=FUk0Z$?#G=B~FYM&3#Za*z4Yg{gb?c$*YUy#0W zXnL}=-Q25qL(W=;=VoOxTMv)@r0Db1pj(`c8}HQ&^Tu1bds-JR(Uv3F6kuKJqUdCL zM8Q6TaIuh!q=Yt*{!r6-hwrE-WKBN`LSQa2C(xgKt7V;vWZugN;i!6+FJmy!(bO4u zb08;rkuUUR3ttO(P(|MCM*mk~6}sY(Sx1BRIZ3l2#q(DuW71-T_jM~5@;q}t^D-5_ z6@^I~mECL!3H*{1@Hre74ZcCcCf~f-CuuQT#*X(ZlC|*z&l?I(w|V9(K{IxobFrFm zAAQJdp#_ZjP`|Rw=62+;safL%^u&+1k|ZZ-Bo+xQC}Cvw@-P%usTHd>j2%Pjj7XR} zQ=8*EI(SSsT{DR{T`2<-f_OKESdlNIeE9Z!)_MIyQCIS@98g7-*~KI`J!K>3P!DtOB2AVGZ~+#Nf1q^l^Oq)MqM80*JL)VkTf=0&V^v2CP3b1&(|u#D~>qUOE5E zM>yKr$NP8(VG}!44crfFRK3rYn(cU?aD^P3DqY&-YK+FW4E4F7*KJWk1p+5Cm_XuMw+e-zgiE^$lU`|>a zgcsyVFEehTKzKD!f9)wpt2(LxRNDh(V_eZ~im-reNd3_4Elz!dj;dfm=(@w~_ z^q=0hWprR4!U+&U+CgWo1l?RXGbMbeIzt3b%e*|)^b7=`r@JQq_lH4 z`sWO%%V+g`)4#4H*#)F~e;>+CQ@I{oI~Ho_<2vqTwTj@(V?#h=hNBA`m;4?nX=+-g z*Z-_@i1No;fI;AI-=RU^Q#U2y6E%=8mv<&Ra8BTpTH4T6PFf!G)A^KwdxBGMZ0xGN zo+Dy({xQ1|OiS-92iBPO@}N6bIxxGdpz*1HSnSFFve;a#>t}=gJSjKM#0&T%a~Cb& z<%!%h@5Rg)%-jXypvd@{EeDs>&-6RY9|^58 z0ao(2hxlG@LCe^W_dP%UhS^xb7#SG;|DTQrJ|K?)27NjB^Y3Tn*;p~NK;MCZzuMq` J@e9za{{w4MBEtXx literal 0 HcmV?d00001 diff --git a/src/addresses.rs b/src/addresses.rs new file mode 100644 index 0000000..4947df5 --- /dev/null +++ b/src/addresses.rs @@ -0,0 +1,14 @@ +extern crate rand; +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct PublicKey { + pub id: u64 +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct SecretKey {} + +pub fn gen_keys() -> (PublicKey, SecretKey) { + (PublicKey { id: rand::random::() }, SecretKey {}) +} diff --git a/src/ironforce.rs b/src/ironforce.rs new file mode 100644 index 0000000..eed2ccf --- /dev/null +++ b/src/ironforce.rs @@ -0,0 +1,16 @@ +use crate::transport::Transport; +use crate::addresses::{PublicKey, SecretKey, gen_keys}; + + +pub struct IronForce { + transport: Transport, + public: PublicKey, + secret: SecretKey, +} + +impl IronForce { + pub fn new() -> IronForce { + let (public, secret) = gen_keys(); + IronForce { transport: Transport {}, public, secret } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c83265b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,33 @@ +#![feature(alloc)] +#![no_std] + +extern crate alloc; + +mod ironforce; +mod transport; +mod message; +mod addresses; + +#[cfg(test)] +mod tests { + use crate::ironforce::IronForce; + use crate::message::{MsgType, Message}; + use alloc::vec::Vec; + + #[test] + fn creation_works() { + IronForce::new(); + } + + #[test] + fn serialization() { + let msg = crate::message::Message { + msg_type: MsgType::MultiCast, + hash: 0, + body: 0, + source: crate::addresses::PublicKey { id: 0 }, + }; + let serialized = msg.ser(); + let msg2 = Message::deserialize(serialized); + } +} diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000..feda78b --- /dev/null +++ b/src/message.rs @@ -0,0 +1,36 @@ +use serde::{Serialize, Deserialize}; +use crate::addresses::PublicKey; +use alloc::vec::Vec; + + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum MsgType { + MultiCast, + ToTarget(PublicKey), +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct Message { + pub msg_type: MsgType, + pub hash: u64, + pub body: u64, + pub source: PublicKey, +} + +impl Message { + pub fn ser(self) -> Vec { + let mut buf = [0u8; 8192]; + let mut ser = serde_cbor::Serializer::new( + serde_cbor::ser::SliceWrite::new(&mut buf[..]) + ); + self.serialize(&mut ser).unwrap(); + let writer = ser.into_inner(); + buf.to_vec() + } + + pub fn deserialize(serialized: Vec) -> Self { + let mut scratch = [0u8; 8192]; + let msg = serde_cbor::de::from_slice_with_scratch(&serialized.as_slice(), &mut scratch).unwrap(); + msg + } +} diff --git a/src/transport.rs b/src/transport.rs new file mode 100644 index 0000000..e3f5577 --- /dev/null +++ b/src/transport.rs @@ -0,0 +1,2 @@ + +pub(crate) struct Transport {}