summary refs log tree commit diff
path: root/crypto/test/src
diff options
context:
space:
mode:
authorroyb <roy.basmacier@primekey.com>2022-02-03 12:51:52 -0500
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-06-23 21:58:04 +0700
commit004de388d03ebfc6734d4a613f5114ceb8f7a570 (patch)
treec796413a7589c47548c15f35ec4b27f4b17fe6a8 /crypto/test/src
parentNew build organization (diff)
downloadBouncyCastle.NET-ed25519-004de388d03ebfc6734d4a613f5114ceb8f7a570.tar.xz
Initial merge of PQC port
Diffstat (limited to 'crypto/test/src')
-rw-r--r--crypto/test/src/pqc/crypto/lms/HSSTests.cs904
-rw-r--r--crypto/test/src/pqc/crypto/lms/LMSKeyGenTests.cs149
-rw-r--r--crypto/test/src/pqc/crypto/lms/LMSTests.cs177
-rw-r--r--crypto/test/src/pqc/crypto/lms/LMSVectorUtils.cs38
-rw-r--r--crypto/test/src/pqc/crypto/lms/TypeTests.cs71
-rw-r--r--crypto/test/src/pqc/crypto/test/CmceVectorTest.cs172
-rw-r--r--crypto/test/src/pqc/crypto/test/FrodoVectorTest.cs165
-rw-r--r--crypto/test/src/pqc/crypto/test/HSSTest.cs146
-rw-r--r--crypto/test/src/pqc/crypto/test/LMSTest.cs113
-rw-r--r--crypto/test/src/pqc/crypto/test/NistSecureRandom.cs192
-rw-r--r--crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs173
-rw-r--r--crypto/test/src/pqc/crypto/test/SaberVectorTest.cs146
-rw-r--r--crypto/test/src/pqc/crypto/test/SphincsPlusTest.cs448
13 files changed, 2894 insertions, 0 deletions
diff --git a/crypto/test/src/pqc/crypto/lms/HSSTests.cs b/crypto/test/src/pqc/crypto/lms/HSSTests.cs
new file mode 100644
index 000000000..6918da102
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/lms/HSSTests.cs
@@ -0,0 +1,904 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using NUnit.Framework;
+using Org.BouncyCastle.Bcpg;
+using Org.BouncyCastle.Pqc.Crypto.Lms;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.Utilities.Test;
+
+
+namespace Org.BouncyCastle.Pqc.Crypto.Lms
+{
+
+    [TestFixture]
+    public class HSSTests
+    {
+
+        [Test]
+        public void TestHssKeySerialisation()
+        {
+            byte[] fixedSource = new byte[8192];
+            for (int t = 0; t < fixedSource.Length; t++)
+            {
+                fixedSource[t] = 1;
+            }
+
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(fixedSource)};
+            SecureRandom rand = new FixedSecureRandom(source);
+
+
+            HSSPrivateKeyParameters generatedPrivateKey = HSS.GenerateHssKeyPair(
+                new HSSKeyGenerationParameters(new LMSParameters[]
+                {
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2),
+                }, rand)
+            );
+
+            HSSSignature sigFromGeneratedPrivateKey = HSS.GenerateSignature(generatedPrivateKey, Hex.Decode("ABCDEF"));
+
+            byte[] keyPairEnc = generatedPrivateKey.GetEncoded();
+
+            HSSPrivateKeyParameters reconstructedPrivateKey = HSSPrivateKeyParameters.GetInstance(keyPairEnc);
+            Assert.True(reconstructedPrivateKey.Equals(generatedPrivateKey));
+
+
+            reconstructedPrivateKey.GetPublicKey();
+            generatedPrivateKey.GetPublicKey();
+
+            //
+            // Are they still equal, public keys are only checked if they both
+            // exist because they are only created when requested as they are derived from the private key.
+            //
+            Assert.True(reconstructedPrivateKey.Equals(generatedPrivateKey));
+
+            //
+            // Check the reconstructed key can verify a signature.
+            //
+            Assert.True(HSS.VerifySignature(reconstructedPrivateKey.GetPublicKey(), sigFromGeneratedPrivateKey,
+                Hex.Decode("ABCDEF")));
+
+        }
+
+
+        /**
+         * Test Case 1 Signature
+         * From https://tools.ietf.org/html/rfc8554#appendix-F
+         *
+         * @
+         */
+        [Test]
+        public void TestHSSVector_1()
+        {
+            IList blocks = LoadVector("pqc.lms.testcase_1.txt");
+
+            HSSPublicKeyParameters publicKey = HSSPublicKeyParameters.GetInstance(blocks[0]);
+            byte[] message = (byte[]) blocks[1];
+            HSSSignature signature = HSSSignature.GetInstance(blocks[2], publicKey.GetL());
+            Assert.True(HSS.VerifySignature(publicKey, signature, message), "Test Case 1 ");
+        }
+
+        /**
+         * Test Case 1 Signature
+         * From https://tools.ietf.org/html/rfc8554#appendix-F
+         *
+         * @
+         */
+        [Test]
+        public void TestHSSVector_2()
+        {
+            IList blocks = LoadVector("pqc.lms.testcase_2.txt");
+
+            HSSPublicKeyParameters publicKey = HSSPublicKeyParameters.GetInstance(blocks[0]);
+            byte[] message = blocks[1] as byte[];
+            byte[] sig = blocks[2] as byte[];
+            HSSSignature signature = HSSSignature.GetInstance(sig, publicKey.GetL());
+            Assert.True(HSS.VerifySignature(publicKey, signature, message), "Test Case 2 Signature");
+
+            LMSPublicKeyParameters lmsPub = LMSPublicKeyParameters.GetInstance(blocks[3]);
+            LMSSignature lmsSignature = LMSSignature.GetInstance(blocks[4]);
+
+            Assert.True(LMS.VerifySignature(lmsPub, lmsSignature, message), "Test Case 2 Signature 2");
+        }
+
+        private IList LoadVector(string vector)
+        {
+            StreamReader bin = new StreamReader(SimpleTest.GetTestDataAsStream(vector));
+            IList blocks = new ArrayList();
+            StringBuilder sw = new StringBuilder();
+
+            string line;
+            while ((line = bin.ReadLine()) != null)
+            {
+                if (line.StartsWith("!"))
+                {
+                    if (sw.Length > 0)
+                    {
+                        blocks.Add(LMSVectorUtils.ExtractPrefixedBytes(sw.ToString()));
+                        sw.Length = 0;
+                    }
+                }
+                sw.Append(line);
+                sw.Append("\n");
+            }
+
+            if (sw.Length > 0)
+            {
+                blocks.Add(LMSVectorUtils.ExtractPrefixedBytes(sw.ToString()));
+                sw.Length = 0;
+            }
+            return blocks;
+        }
+
+
+        /**
+         * Test the generation of public keys from private key SEED and I.
+         * Level 0
+         *
+         * @
+         */
+        [Test]
+        public void TestGenPublicKeys_L0()
+        {
+            byte[] seed = Hex.Decode("558b8966c48ae9cb898b423c83443aae014a72f1b1ab5cc85cf1d892903b5439");
+            int level = 0;
+            LMSPrivateKeyParameters lmsPrivateKey = LMS.GenerateKeys(LMSigParameters.GetParametersForType(6),
+                LMOtsParameters.GetParametersForType(3), level, Hex.Decode("d08fabd4a2091ff0a8cb4ed834e74534"), seed);
+            LMSPublicKeyParameters publicKey = lmsPrivateKey.GetPublicKey();
+            Assert.True(Arrays.AreEqual(publicKey.GetT1(),
+                Hex.Decode("32a58885cd9ba0431235466bff9651c6c92124404d45fa53cf161c28f1ad5a8e")));
+            Assert.True(Arrays.AreEqual(publicKey.GetI(), Hex.Decode("d08fabd4a2091ff0a8cb4ed834e74534")));
+        }
+
+        /**
+         * Test the generation of public keys from private key SEED and I.
+         * Level 1;
+         *
+         * @
+         */
+        [Test]
+        public void TestGenPublicKeys_L1()
+        {
+            byte[] seed = Hex.Decode("a1c4696e2608035a886100d05cd99945eb3370731884a8235e2fb3d4d71f2547");
+            int level = 1;
+            LMSPrivateKeyParameters lmsPrivateKey = LMS.GenerateKeys(LMSigParameters.GetParametersForType(5),
+                LMOtsParameters.GetParametersForType(4), level, Hex.Decode("215f83b7ccb9acbcd08db97b0d04dc2b"), seed);
+            LMSPublicKeyParameters publicKey = lmsPrivateKey.GetPublicKey();
+            Assert.True(Arrays.AreEqual(publicKey.GetT1(),
+                Hex.Decode("a1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b7")));
+            Assert.True(Arrays.AreEqual(publicKey.GetI(), Hex.Decode("215f83b7ccb9acbcd08db97b0d04dc2b")));
+        }
+
+        [Test]
+        public void TestGenerate()
+        {
+            //
+            // Generate an HSS key pair for a two level HSS scheme.
+            // then use that to verify it compares with a value from the same reference implementation.
+            // Then check components of it serialize and deserialize properly.
+            //
+
+            byte[] fixedSource = new byte[8192];
+            for (int t = 0; t < fixedSource.Length; t++)
+            {
+                fixedSource[t] = 1;
+            }
+
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(fixedSource)};
+            SecureRandom rand = new FixedSecureRandom(source);
+
+            HSSPrivateKeyParameters keyPair = HSS.GenerateHssKeyPair(
+                new HSSKeyGenerationParameters(new LMSParameters[]
+                {
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2),
+                }, rand));
+
+
+            //
+            // Generated from reference implementation.
+            // check the encoded form of the public key matches.
+            //
+            string expectedPk =
+                "0000000200000005000000030101010101010101010101010101010166BF6F5816EEE4BBF33C50ACB480E09B4169EBB533372959BC4315C388E501AC";
+            byte[] pkEnc = keyPair.GetPublicKey().GetEncoded();
+            Assert.True(Arrays.AreEqual(Hex.Decode(expectedPk), pkEnc));
+
+            //
+            // Check that HSS public keys have value equality after deserialization.
+            // Use external sourced pk for deserialization.
+            //
+            Assert.True(keyPair.GetPublicKey().Equals(HSSPublicKeyParameters.GetInstance(Hex.Decode(expectedPk))),
+                "HSSPrivateKeyParameterss equal are deserialization");
+
+
+            //
+            // Generate, hopefully the same HSSKetPair for the same entropy.
+            // This is a sanity test
+            //
+            {
+                FixedSecureRandom.Source[] source1 = {new FixedSecureRandom.Source(fixedSource)};
+                SecureRandom rand1 = new FixedSecureRandom(source1);
+
+                HSSPrivateKeyParameters regenKeyPair = HSS.GenerateHssKeyPair(
+                    new HSSKeyGenerationParameters(new LMSParameters[]
+                    {
+                        new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                        new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2),
+                    }, rand1));
+
+
+                Assert.True(
+                    Arrays.AreEqual(regenKeyPair.GetPublicKey().GetEncoded(), keyPair.GetPublicKey().GetEncoded()),
+                    "Both generated keys are the same");
+
+                Assert.True(keyPair.GetKeys().Count == regenKeyPair.GetKeys().Count,
+                    "same private key size");
+
+                for (int t = 0; t < keyPair.GetKeys().Count; t++)
+                {
+                    //
+                    // Check the private keys can be encoded and are the same.
+                    //
+                    byte[] pk1 = (keyPair.GetKeys()[t] as LMSPrivateKeyParameters).GetEncoded();
+                    byte[] pk2 = (regenKeyPair.GetKeys()[t] as LMSPrivateKeyParameters).GetEncoded();
+                    Assert.True(Arrays.AreEqual(pk1, pk2));
+
+                    //
+                    // Deserialize them and see if they still equal.
+                    //
+                    LMSPrivateKeyParameters pk1O = LMSPrivateKeyParameters.GetInstance(pk1);
+                    LMSPrivateKeyParameters pk2O = LMSPrivateKeyParameters.GetInstance(pk2);
+
+                    Assert.True(pk1O.Equals(pk2O), "LmsPrivateKey still equal after deserialization");
+
+                }
+            }
+
+            //
+            // This time we will generate another set of keys using a different entropy source.
+            // they should be different!
+            // Useful for detecting accidental hard coded things.
+            //
+
+            {
+                // Use a real secure random this time.
+                SecureRandom rand1 = new SecureRandom();
+
+                HSSPrivateKeyParameters differentKey = HSS.GenerateHssKeyPair(
+                    new HSSKeyGenerationParameters(new LMSParameters[]
+                    {
+                        new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                        new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2),
+                    }, rand1)
+                );
+
+
+                Assert.False(
+                    Arrays.AreEqual(differentKey.GetPublicKey().GetEncoded(), keyPair.GetPublicKey().GetEncoded()),
+                    "Both generated keys are not the same");
+
+
+                for (int t = 0; t < keyPair.GetKeys().Count; t++)
+                {
+                    //
+                    // Check the private keys can be encoded and are not the same.
+                    //
+                    byte[] pk1 = (keyPair.GetKeys()[t] as LMSPrivateKeyParameters).GetEncoded();
+                    byte[] pk2 = (differentKey.GetKeys()[t] as LMSPrivateKeyParameters).GetEncoded();
+                    Assert.False(Arrays.AreEqual(pk1, pk2), "keys not the same");
+
+                    //
+                    // Deserialize them and see if they still equal.
+                    //
+                    LMSPrivateKeyParameters pk1O = LMSPrivateKeyParameters.GetInstance(pk1);
+                    LMSPrivateKeyParameters pk2O = LMSPrivateKeyParameters.GetInstance(pk2);
+
+                    Assert.False(pk1O.Equals(pk2O), "LmsPrivateKey not suddenly equal after deserialization");
+
+                }
+
+            }
+
+        }
+
+
+        /**
+         * This test takes in a series of vectors generated by adding print statements to code called by
+         * the "test_sign.c" test in the reference implementation.
+         * <p>
+         * The purpose of this test is to ensure that the signatures and public keys exactly match for the
+         * same entropy source the values generated by the reference implementation.
+         * <p>
+         * It also verifies value equality between signature and public key objects as well as
+         * complimentary serialization and deserialization.
+         *
+         * @
+         */
+        [Test]
+        public void TestVectorsFromReference()
+        {
+            StreamReader sr = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.lms.depth_1.txt"));
+
+            IList lmsParameters = new ArrayList();
+            IList lmOtsParameters = new ArrayList();
+            byte[] message = null;
+            byte[] hssPubEnc = null;
+            MemoryStream fixedESBuffer = new MemoryStream();
+            int d = 0, j = 0;
+
+            string line;
+            while ((line = sr.ReadLine()) != null)
+            {
+                if (TrimLine(ref line))
+                    continue;
+
+                if (line.StartsWith("Depth:"))
+                {
+                    d = int.Parse(line.Substring("Depth:".Length).Trim());
+                }
+                else if (line.StartsWith("LMType:"))
+                {
+                    int typ = int.Parse(line.Substring("LMType:".Length).Trim());
+                    lmsParameters.Add(LMSigParameters.GetParametersForType(typ));
+                }
+                else if (line.StartsWith("LMOtsType:"))
+                {
+                    int typ = int.Parse(line.Substring("LMOtsType:".Length).Trim());
+                    lmOtsParameters.Add(LMOtsParameters.GetParametersForType(typ));
+                }
+                else if (line.StartsWith("Rand:"))
+                {
+                    var b = Hex.Decode(line.Substring("Rand:".Length).Trim());
+                    fixedESBuffer.Write(b, 0, b.Length);
+                }
+                else if (line.StartsWith("HSSPublicKey:"))
+                {
+                    hssPubEnc = Hex.Decode(line.Substring("HSSPublicKey:".Length).Trim());
+                }
+                else if (line.StartsWith("Message:"))
+                {
+                    message = Hex.Decode(line.Substring("Message:".Length).Trim());
+                }
+                else if (line.StartsWith("Signature:"))
+                {
+                    j++;
+
+                    byte[] encodedSigFromVector = Hex.Decode(line.Substring("Signature:".Length).Trim());
+
+                    //
+                    // Assumes Signature is the last element in the set of vectors.
+                    //
+                    FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(fixedESBuffer.ToArray())};
+                    FixedSecureRandom fixRnd = new FixedSecureRandom(source);
+                    fixedESBuffer.SetLength(0);//todo is this correct? buffer.reset();
+                    //fixedESBuffer = new MemoryStream();
+
+                    //
+                    // Deserialize pub key from reference impl.
+                    //
+                    HSSPublicKeyParameters vectorSourcedPubKey = HSSPublicKeyParameters.GetInstance(hssPubEnc);
+                    IList lmsParams = new ArrayList();
+
+                    for (int i = 0; i != lmsParameters.Count; i++)
+                    {
+                        lmsParams.Add(new LMSParameters(lmsParameters[i] as LMSigParameters,
+                            lmOtsParameters[i] as LMOtsParameters));
+                    }
+
+                    //
+                    // Using our fixed entropy source generate hss keypair
+                    //
+
+                    LMSParameters[] lmsParamsArray = new LMSParameters[lmsParams.Count];
+                    lmsParams.CopyTo(lmsParamsArray, 0);
+                    HSSPrivateKeyParameters keyPair = HSS.GenerateHssKeyPair(
+                        new HSSKeyGenerationParameters(
+                            lmsParamsArray, fixRnd)
+                    );
+
+                    {
+                        // Public Key should match vector.
+
+                        // Encoded value equality.
+                        HSSPublicKeyParameters generatedPubKey = keyPair.GetPublicKey();
+                        Assert.True(Arrays.AreEqual(hssPubEnc, generatedPubKey.GetEncoded()));
+
+                        // Value equality.
+                        Assert.True(vectorSourcedPubKey.Equals(generatedPubKey));
+                    }
+
+
+                    //
+                    // Generate a signature using the keypair we generated.
+                    //
+                    HSSSignature sig = HSS.GenerateSignature(keyPair, message);
+
+                    HSSSignature signatureFromVector = null;
+                    if (!Arrays.AreEqual(sig.GetEncoded(), encodedSigFromVector))
+                    {
+                        signatureFromVector = HSSSignature.GetInstance(encodedSigFromVector, d);
+                        signatureFromVector.Equals(sig);
+                    }
+
+                    // check encoding signature matches.
+                    Assert.True(Arrays.AreEqual(sig.GetEncoded(), encodedSigFromVector));
+
+                    // Check we can verify our generated signature with the vectors sourced public key.
+                    Assert.True(HSS.VerifySignature(vectorSourcedPubKey, sig, message));
+
+                    // Deserialize the signature from the vector.
+                    signatureFromVector = HSSSignature.GetInstance(encodedSigFromVector, d);
+
+                    // Can we verify signature from vector with public key from vector.
+                    Assert.True(HSS.VerifySignature(vectorSourcedPubKey, signatureFromVector, message));
+
+                    //
+                    // Check our generated signature and the one deserialized from the vector
+                    // have value equality.
+                    Assert.True(signatureFromVector.Equals(sig));
+
+
+                    //
+                    // Other tests vandalise HSS signatures to check they Assert.Fail when tampered with
+                    // we won't do that again here.
+                    //
+
+                    d = 0;
+                    lmOtsParameters.Clear();
+                    lmsParameters.Clear();
+                    message = null;
+                    hssPubEnc = null;
+                }
+            }
+        }
+
+        [Test]
+        public void TestVectorsFromReference_Expanded()
+        {
+            using (StreamReader sr = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.lms.expansion.txt")))
+            {
+                IList lmsParameters = new ArrayList();
+                IList lmOtsParameters = new ArrayList();
+                byte[] message = null;
+                byte[] hssPubEnc = null;
+                MemoryStream fixedESBuffer = new MemoryStream();
+                IList sigVectors = new ArrayList();
+                int d = 0, j = 0;
+
+                string line;
+                while ((line = sr.ReadLine()) != null)
+                {
+                    if (TrimLine(ref line))
+                        continue;
+
+                    if (line.StartsWith("Depth:"))
+                    {
+                        d = int.Parse(line.Substring("Depth:".Length).Trim());
+                    }
+                    else if (line.StartsWith("LMType:"))
+                    {
+                        int typ = int.Parse(line.Substring("LMType:".Length).Trim());
+                        lmsParameters.Add(LMSigParameters.GetParametersForType(typ));
+                    }
+                    else if (line.StartsWith("LMOtsType:"))
+                    {
+                        int typ = int.Parse(line.Substring("LMOtsType:".Length).Trim());
+                        lmOtsParameters.Add(LMOtsParameters.GetParametersForType(typ));
+                    }
+                    else if (line.StartsWith("Rand:"))
+                    {
+                        var b = Hex.Decode(line.Substring("Rand:".Length).Trim());
+                        fixedESBuffer.Write(b, 0, b.Length);
+                    }
+                    else if (line.StartsWith("HSSPublicKey:"))
+                    {
+                        hssPubEnc = Hex.Decode(line.Substring("HSSPublicKey:".Length).Trim());
+                    }
+                    else if (line.StartsWith("Message:"))
+                    {
+                        message = Hex.Decode(line.Substring("Message:".Length).Trim());
+
+                    }
+                    else if (line.StartsWith("Signature:"))
+                    {
+                        sigVectors.Add(Hex.Decode(line.Substring("Signature:".Length).Trim()));
+                    }
+                }
+
+                //
+                // Assumes Signature is the last element in the set of vectors.
+                //
+                FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(fixedESBuffer.ToArray())};
+                FixedSecureRandom fixRnd = new FixedSecureRandom(source);
+                fixedESBuffer.SetLength(0);
+                IList lmsParams = new ArrayList();
+
+                for (int i = 0; i != lmsParameters.Count; i++)
+                {
+                    lmsParams.Add(new LMSParameters(lmsParameters[i] as LMSigParameters,
+                        lmOtsParameters[i] as LMOtsParameters));
+                }
+
+                LMSParameters[] lmsParamsArray = new LMSParameters[lmsParams.Count];
+                lmsParams.CopyTo(lmsParamsArray, 0);
+                HSSPrivateKeyParameters keyPair = HSS.GenerateHssKeyPair(
+                    new HSSKeyGenerationParameters(
+                        lmsParamsArray, fixRnd)
+                );
+
+                Assert.True(Arrays.AreEqual(hssPubEnc, keyPair.GetPublicKey().GetEncoded()));
+
+                HSSPublicKeyParameters pubKeyFromVector = HSSPublicKeyParameters.GetInstance(hssPubEnc);
+                HSSPublicKeyParameters pubKeyGenerated = null;
+
+
+                Assert.AreEqual(1024, keyPair.GetUsagesRemaining());
+                Assert.AreEqual(1024, keyPair.IndexLimit);
+
+                //
+                // Split the space up with a shard.
+                //
+
+                HSSPrivateKeyParameters shard1 = keyPair.ExtractKeyShard(500);
+                pubKeyGenerated = shard1.GetPublicKey();
+
+
+                HSSPrivateKeyParameters pair = shard1;
+
+                int c = 0;
+                for (int i = 0; i < keyPair.IndexLimit; i++)
+                {
+                    if (i == 500)
+                    {
+                        try
+                        {
+                            HSS.IncrementIndex(pair);
+                            Assert.Fail("shard should be exhausted.");
+                        }
+                        catch (Exception ex)
+                        {
+                            Assert.AreEqual("hss private key shard is exhausted", ex.Message);
+                        }
+
+                        pair = keyPair;
+                        pubKeyGenerated = keyPair.GetPublicKey();
+
+                        Assert.AreEqual(pubKeyGenerated, shard1.GetPublicKey());
+                    }
+
+                    if (i % 5 == 0)
+                    {
+                        HSSSignature sigCalculated = HSS.GenerateSignature(pair, message);
+                        Assert.True(Arrays.AreEqual(sigCalculated.GetEncoded(),(byte[]) sigVectors[c]));
+
+                        Assert.True(HSS.VerifySignature(pubKeyFromVector, sigCalculated, message));
+                        Assert.True(HSS.VerifySignature(pubKeyGenerated, sigCalculated, message));
+
+                        HSSSignature sigFromVector = HSSSignature.GetInstance((byte[]) sigVectors[c],
+                            pubKeyFromVector.GetL());
+
+                        Assert.True(HSS.VerifySignature(pubKeyFromVector, sigFromVector, message));
+                        Assert.True(HSS.VerifySignature(pubKeyGenerated, sigFromVector, message));
+
+
+                        Assert.True(sigCalculated.Equals(sigFromVector));
+
+
+                        c++;
+                    }
+                    else
+                    {
+                        HSS.IncrementIndex(pair);
+                    }
+                }
+            }
+        }
+        
+        /**
+         * Test remaining calculation is accurate and a new key is generated when
+         * all the ots keys for that level are consumed.
+         *
+         * @
+         */
+        [Test]
+        public void TestRemaining()
+        {
+            HSSPrivateKeyParameters keyPair = HSS.GenerateHssKeyPair(
+                new HSSKeyGenerationParameters(new LMSParameters[]
+                {
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2)
+                }, new SecureRandom())
+            );
+
+
+            LMSPrivateKeyParameters lmsKey = keyPair.GetKeys()[keyPair.L - 1] as LMSPrivateKeyParameters;
+            //
+            // There should be a max of 32768 signatures for this key.
+            //
+            Assert.True(1024 == keyPair.GetUsagesRemaining());
+
+            HSS.IncrementIndex(keyPair);
+            HSS.IncrementIndex(keyPair);
+            HSS.IncrementIndex(keyPair);
+            HSS.IncrementIndex(keyPair);
+            HSS.IncrementIndex(keyPair);
+
+            Assert.True(5 == keyPair.GetIndex()); // Next key is at index 5!
+
+
+            Assert.True(1024 - 5 == keyPair.GetUsagesRemaining());
+
+
+            HSSPrivateKeyParameters shard = keyPair.ExtractKeyShard(10);
+
+            Assert.True(15 == shard.IndexLimit);
+            Assert.True(5 == shard.GetIndex());
+
+            // Should not be the same.
+            Assert.False(shard.GetIndex() == keyPair.GetIndex());
+
+            //
+            // Should be 17 left, it will throw if it has been exhausted.
+            //
+            for (int t = 0; t < 17; t++)
+            {
+                HSS.IncrementIndex(keyPair);
+            }
+
+            // We have used 32 keys.
+            Assert.True(1024 - 32 == keyPair.GetUsagesRemaining());
+
+
+            HSS.GenerateSignature(keyPair, Encoding.ASCII.GetBytes("Foo"));
+
+            //
+            // This should trigger the generation of a new key.
+            //
+            LMSPrivateKeyParameters potentialNewLMSKey = keyPair.GetKeys()[keyPair.L - 1] as LMSPrivateKeyParameters;
+            Assert.False(potentialNewLMSKey.Equals(lmsKey));
+        }
+
+        [Test]
+        public void TestSharding()
+        {
+            HSSPrivateKeyParameters keyPair = HSS.GenerateHssKeyPair(
+                new HSSKeyGenerationParameters(new LMSParameters[]
+                {
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2)
+                }, new SecureRandom())
+            );
+
+            Assert.True(1024 == keyPair.GetUsagesRemaining());
+            Assert.True(1024 == keyPair.IndexLimit);
+            Assert.True(0 == keyPair.GetIndex());
+            Assert.False(keyPair.IsShard());
+            HSS.IncrementIndex(keyPair);
+
+
+            //
+            // Take a shard that should cross boundaries
+            //
+            HSSPrivateKeyParameters shard = keyPair.ExtractKeyShard(48);
+            Assert.True(shard.IsShard());
+            Assert.True(48 == shard.GetUsagesRemaining());
+            Assert.True(49 == shard.IndexLimit);
+            Assert.True(1 == shard.GetIndex());
+
+            Assert.True(49 == keyPair.GetIndex());
+
+
+            int t = 47;
+            while (--t >= 0)
+            {
+                HSS.IncrementIndex(shard);
+            }
+
+            HSSSignature sig = HSS.GenerateSignature(shard, Encoding.ASCII.GetBytes("Cats"));
+
+            //
+            // Test it validates and nothing has gone wrong with the public keys.
+            //
+            Assert.True(HSS.VerifySignature(keyPair.GetPublicKey(), sig, Encoding.ASCII.GetBytes("Cats")));
+            Assert.True(HSS.VerifySignature(shard.GetPublicKey(), sig, Encoding.ASCII.GetBytes("Cats")));
+
+            // Signing again should Assert.Fail.
+
+            try
+            {
+                HSS.GenerateSignature(shard, Encoding.ASCII.GetBytes("Cats"));
+                Assert.Fail();
+            }
+            catch (Exception ex)
+            {
+                Assert.True(ex.Message.Equals("hss private key shard is exhausted"));
+            }
+
+            // Should work without throwing.
+            HSS.GenerateSignature(keyPair, Encoding.ASCII.GetBytes("Cats"));
+        }
+
+        /**
+         * Take an HSS key pair and exhaust its signing capacity.
+         *
+         * @
+         */
+        class HSSSecureRandom
+            : SecureRandom
+        {
+            public override void NextBytes(byte[] bytes)
+            {
+                for (int t = 0; t < bytes.Length; t++)
+                {
+                    bytes[t] = 1;
+                }
+            }
+        }
+        
+        [Test]
+        public void TestSignUnitExhaustion()
+        {
+
+            HSSSecureRandom rand = new HSSSecureRandom();
+
+            HSSPrivateKeyParameters keyPair = HSS.GenerateHssKeyPair(
+                new HSSKeyGenerationParameters(new LMSParameters[]
+                {
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w2),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h10, LMOtsParameters.sha256_n32_w1),
+                }, rand)
+            );
+
+            HSSPublicKeyParameters pk = keyPair.GetPublicKey();
+
+
+            int ctr = 0;
+            byte[] message = new byte[32];
+
+            //
+            // There should be a max of 32768 signatures for this key.
+            //
+
+            Assert.True(keyPair.GetUsagesRemaining() == 32768);
+
+            int mod = 256;
+            try
+            {
+                while (ctr < 32769) // Just a number..
+                {
+
+                    if (ctr % mod == 0)
+                    {
+                        //
+                        // We don't want to check every key.
+                        // The test will take over an hour to complete.
+                        //
+                        Pack_Int32_To_BE(ctr, message, 0);
+                        HSSSignature sig = HSS.GenerateSignature(keyPair, message);
+
+                        Assert.True(ctr % 1024 == sig.GetSignature().GetQ());
+
+                        // Check there was a post increment in the tail end LMS key.
+                        Assert.True((ctr % 1024) + 1 == (keyPair.GetKeys()[keyPair.L - 1] as LMSPrivateKeyParameters).GetIndex());
+
+                        Assert.True(ctr + 1 == keyPair.GetIndex());
+
+
+                        // Validate the heirarchial path building was correct.
+
+                        long[] qValues = new long[keyPair.GetKeys().Count];
+                        long q = ctr;
+
+                        for (int t = keyPair.GetKeys().Count - 1; t >= 0; t--)
+                        {
+                            LMSigParameters sigParameters = (keyPair.GetKeys()[t] as LMSPrivateKeyParameters).GetSigParameters();
+                            int mask = (1 << sigParameters.GetH()) - 1;
+                            qValues[t] = q & mask;
+                            q >>= sigParameters.GetH();
+                        }
+
+                        for (int t = 0; t < keyPair.GetKeys().Count; t++)
+                        {
+                            Assert.True( (keyPair.GetKeys()[t] as LMSPrivateKeyParameters).GetIndex() - 1 == qValues[t]);
+                        }
+
+
+                        Assert.True(HSS.VerifySignature(pk, sig, message));
+                        Assert.True(sig.GetSignature().GetParameter().GetType() ==
+                                    LMSigParameters.lms_sha256_n32_h10.GetType());
+
+                        {
+                            //
+                            // Vandalise hss signature.
+                            //
+                            byte[] rawSig = sig.GetEncoded();
+                            rawSig[100] ^= 1;
+                            HSSSignature parsedSig = HSSSignature.GetInstance(rawSig, pk.GetL());
+                            Assert.False(HSS.VerifySignature(pk, parsedSig, message));
+
+                            try
+                            {
+                                HSSSignature.GetInstance(rawSig, 0);
+                                Assert.Fail();
+                            }
+                            catch (Exception ex)
+                            {
+                                Assert.True(ex.Message.Contains("nspk exceeded maxNspk"));
+                            }
+
+                        }
+
+
+                        {
+                            //
+                            // Vandalise hss message
+                            //
+                            byte[] newMsg = new byte[message.Length];
+                            message.CopyTo(newMsg, 0);
+                            newMsg[1] ^= 1;
+                            Assert.False(HSS.VerifySignature(pk, sig, newMsg));
+                        }
+
+
+                        {
+                            //
+                            // Vandalise public key
+                            //
+                            byte[] pkEnc = pk.GetEncoded();
+                            pkEnc[35] ^= 1;
+                            HSSPublicKeyParameters rebuiltPk = HSSPublicKeyParameters.GetInstance(pkEnc);
+                            Assert.False(HSS.VerifySignature(rebuiltPk, sig, message));
+                        }
+                    }
+                    else
+                    {
+                        // Skip some keys.
+                        HSS.IncrementIndex(keyPair);
+                    }
+
+                    ctr++;
+
+                }
+
+                //System.out.Println(ctr);
+                Assert.Fail();
+            }
+            catch (Exception ex)
+            {
+                Assert.True(keyPair.GetUsagesRemaining() == 0);
+                Assert.True(ctr == 32768);
+                Assert.True(ex.Message.Contains("hss private key is exhausted"));
+            }
+
+        }
+
+        private static void Pack_Int32_To_BE(int n, byte[] bs, int off)
+        {
+            bs[off] = (byte)(n >> 24);
+            bs[off + 1] = (byte)(n >> 16);
+            bs[off + 2] = (byte)(n >> 8);
+            bs[off + 3] = (byte)(n);
+        }
+
+        private static bool TrimLine(ref string line)
+        {
+            int commentPos = line.IndexOf('#');
+            if (commentPos >= 0)
+            {
+                line = line.Substring(0, commentPos);
+            }
+
+            line = line.Trim();
+
+            return line.Length < 1;
+        }
+    }
+}
diff --git a/crypto/test/src/pqc/crypto/lms/LMSKeyGenTests.cs b/crypto/test/src/pqc/crypto/lms/LMSKeyGenTests.cs
new file mode 100644
index 000000000..8695fe019
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/lms/LMSKeyGenTests.cs
@@ -0,0 +1,149 @@
+using System;
+using NUnit.Framework;
+using Org.BouncyCastle.Pqc.Crypto.Lms;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Lms
+{
+    [TestFixture]
+    public class LMSKeyGenTests
+    {
+        [Test]
+        public void TestGeneratePrivateKey()
+        {
+            //
+            // This is based on the second set of vectors and the second signature.
+            // We verify the correct private key is generated if the correct public key is
+            // derived from it.
+            //
+            byte[] msg = Hex.Decode("54686520656e756d65726174696f6e20\n" +
+                "696e2074686520436f6e737469747574\n" +
+                "696f6e2c206f66206365727461696e20\n" +
+                "7269676874732c207368616c6c206e6f\n" +
+                "7420626520636f6e7374727565642074\n" +
+                "6f2064656e79206f7220646973706172\n" +
+                "616765206f7468657273207265746169\n" +
+                "6e6564206279207468652070656f706c\n" +
+                "652e0a");
+
+            // From vector.
+            byte[] seed = Hex.Decode("a1c4696e2608035a886100d05cd99945eb3370731884a8235e2fb3d4d71f2547");
+            byte[] I = Hex.Decode("215f83b7ccb9acbcd08db97b0d04dc2b");
+            int level = 1; // This is the second level, we use this because it signs the message.
+
+            // Generate the private key.
+            LMSPrivateKeyParameters lmsPrivateKey = LMS.GenerateKeys(LMSigParameters.GetParametersForType(5), LMOtsParameters.GetParametersForType(4), level, I, seed);
+
+            // This derives the public key.
+            LMSPublicKeyParameters publicKey = lmsPrivateKey.GetPublicKey();
+
+            // From the vector.
+            String pkEnc = "0000000500000004215f83b7ccb9acbcd08db97b0d04dc2ba1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b7";
+
+            // Test public key encoded matched vector.
+            Assert.True(Arrays.AreEqual(Hex.Decode(pkEnc), publicKey.GetEncoded()));
+
+            //
+            // Fast forward and burn off some OTS private keys until we get to key number 4.
+            //
+            lmsPrivateKey.ExtractKeyShard(3);
+
+            LMSSignature signature = LMS.GenerateSign(lmsPrivateKey, msg);
+
+            // The expected signature as encoded.
+            String sigEnc = "00000004\n" +
+                "00000004\n" +
+                "0eb1ed54a2460d512388cad533138d24\n" +
+                "0534e97b1e82d33bd927d201dfc24ebb\n" +
+                "11b3649023696f85150b189e50c00e98\n" +
+                "850ac343a77b3638319c347d7310269d\n" +
+                "3b7714fa406b8c35b021d54d4fdada7b\n" +
+                "9ce5d4ba5b06719e72aaf58c5aae7aca\n" +
+                "057aa0e2e74e7dcfd17a0823429db629\n" +
+                "65b7d563c57b4cec942cc865e29c1dad\n" +
+                "83cac8b4d61aacc457f336e6a10b6632\n" +
+                "3f5887bf3523dfcadee158503bfaa89d\n" +
+                "c6bf59daa82afd2b5ebb2a9ca6572a60\n" +
+                "67cee7c327e9039b3b6ea6a1edc7fdc3\n" +
+                "df927aade10c1c9f2d5ff446450d2a39\n" +
+                "98d0f9f6202b5e07c3f97d2458c69d3c\n" +
+                "8190643978d7a7f4d64e97e3f1c4a08a\n" +
+                "7c5bc03fd55682c017e2907eab07e5bb\n" +
+                "2f190143475a6043d5e6d5263471f4ee\n" +
+                "cf6e2575fbc6ff37edfa249d6cda1a09\n" +
+                "f797fd5a3cd53a066700f45863f04b6c\n" +
+                "8a58cfd341241e002d0d2c0217472bf1\n" +
+                "8b636ae547c1771368d9f317835c9b0e\n" +
+                "f430b3df4034f6af00d0da44f4af7800\n" +
+                "bc7a5cf8a5abdb12dc718b559b74cab9\n" +
+                "090e33cc58a955300981c420c4da8ffd\n" +
+                "67df540890a062fe40dba8b2c1c548ce\n" +
+                "d22473219c534911d48ccaabfb71bc71\n" +
+                "862f4a24ebd376d288fd4e6fb06ed870\n" +
+                "5787c5fedc813cd2697e5b1aac1ced45\n" +
+                "767b14ce88409eaebb601a93559aae89\n" +
+                "3e143d1c395bc326da821d79a9ed41dc\n" +
+                "fbe549147f71c092f4f3ac522b5cc572\n" +
+                "90706650487bae9bb5671ecc9ccc2ce5\n" +
+                "1ead87ac01985268521222fb9057df7e\n" +
+                "d41810b5ef0d4f7cc67368c90f573b1a\n" +
+                "c2ce956c365ed38e893ce7b2fae15d36\n" +
+                "85a3df2fa3d4cc098fa57dd60d2c9754\n" +
+                "a8ade980ad0f93f6787075c3f680a2ba\n" +
+                "1936a8c61d1af52ab7e21f416be09d2a\n" +
+                "8d64c3d3d8582968c2839902229f85ae\n" +
+                "e297e717c094c8df4a23bb5db658dd37\n" +
+                "7bf0f4ff3ffd8fba5e383a48574802ed\n" +
+                "545bbe7a6b4753533353d73706067640\n" +
+                "135a7ce517279cd683039747d218647c\n" +
+                "86e097b0daa2872d54b8f3e508598762\n" +
+                "9547b830d8118161b65079fe7bc59a99\n" +
+                "e9c3c7380e3e70b7138fe5d9be255150\n" +
+                "2b698d09ae193972f27d40f38dea264a\n" +
+                "0126e637d74ae4c92a6249fa103436d3\n" +
+                "eb0d4029ac712bfc7a5eacbdd7518d6d\n" +
+                "4fe903a5ae65527cd65bb0d4e9925ca2\n" +
+                "4fd7214dc617c150544e423f450c99ce\n" +
+                "51ac8005d33acd74f1bed3b17b7266a4\n" +
+                "a3bb86da7eba80b101e15cb79de9a207\n" +
+                "852cf91249ef480619ff2af8cabca831\n" +
+                "25d1faa94cbb0a03a906f683b3f47a97\n" +
+                "c871fd513e510a7a25f283b196075778\n" +
+                "496152a91c2bf9da76ebe089f4654877\n" +
+                "f2d586ae7149c406e663eadeb2b5c7e8\n" +
+                "2429b9e8cb4834c83464f079995332e4\n" +
+                "b3c8f5a72bb4b8c6f74b0d45dc6c1f79\n" +
+                "952c0b7420df525e37c15377b5f09843\n" +
+                "19c3993921e5ccd97e097592064530d3\n" +
+                "3de3afad5733cbe7703c5296263f7734\n" +
+                "2efbf5a04755b0b3c997c4328463e84c\n" +
+                "aa2de3ffdcd297baaaacd7ae646e44b5\n" +
+                "c0f16044df38fabd296a47b3a838a913\n" +
+                "982fb2e370c078edb042c84db34ce36b\n" +
+                "46ccb76460a690cc86c302457dd1cde1\n" +
+                "97ec8075e82b393d542075134e2a17ee\n" +
+                "70a5e187075d03ae3c853cff60729ba4\n" +
+                "00000005\n" +
+                "4de1f6965bdabc676c5a4dc7c35f97f8\n" +
+                "2cb0e31c68d04f1dad96314ff09e6b3d\n" +
+                "e96aeee300d1f68bf1bca9fc58e40323\n" +
+                "36cd819aaf578744e50d1357a0e42867\n" +
+                "04d341aa0a337b19fe4bc43c2e79964d\n" +
+                "4f351089f2e0e41c7c43ae0d49e7f404\n" +
+                "b0f75be80ea3af098c9752420a8ac0ea\n" +
+                "2bbb1f4eeba05238aef0d8ce63f0c6e5\n" +
+                "e4041d95398a6f7f3e0ee97cc1591849\n" +
+                "d4ed236338b147abde9f51ef9fd4e1c1";
+
+
+            // Check generated signature matches vector.
+            Assert.True(Arrays.AreEqual(Hex.Decode(sigEnc), signature.GetEncoded()));
+
+
+            // Sanity test
+            Assert.True(LMS.VerifySignature(publicKey, signature, msg));
+            
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/lms/LMSTests.cs b/crypto/test/src/pqc/crypto/lms/LMSTests.cs
new file mode 100644
index 000000000..4b5d6206f
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/lms/LMSTests.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Reflection;
+using NUnit.Framework;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Tests;
+using Org.BouncyCastle.Pqc.Crypto.Lms;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Lms
+{
+    [TestFixture]
+    public class LMSTests
+    {
+        [Test]
+        public void TestCoefFunc()
+        {
+            byte[] S = Hex.Decode("1234");
+            Assert.AreEqual(0, LM_OTS.Coef(S, 7, 1));
+            Assert.AreEqual(1, LM_OTS.Coef(S, 0, 4));
+        }
+
+        [Test]
+        public void TestPrivateKeyRound()
+        {
+            LMOtsParameters parameter = LMOtsParameters.sha256_n32_w4;
+
+            byte[] seed = Hex.Decode("558b8966c48ae9cb898b423c83443aae014a72f1b1ab5cc85cf1d892903b5439");
+            byte[] I = Hex.Decode("d08fabd4a2091ff0a8cb4ed834e74534");
+
+            LMOtsPrivateKey privateKey = new LMOtsPrivateKey(parameter, I, 0, seed);
+            LMOtsPublicKey publicKey = LM_OTS.lms_ots_generatePublicKey(privateKey);
+
+            byte[] ms = new byte[32];
+            for (int t = 0; t < ms.Length; t++)
+            {
+                ms[t] = (byte) t;
+            }
+
+            LMSContext ctx = privateKey.GetSignatureContext(null, null);
+            
+            ctx.BlockUpdate(ms, 0, ms.Length);
+
+            LMOtsSignature sig = LM_OTS.lm_ots_generate_signature(privateKey, ctx.GetQ(), ctx.C);
+            Assert.True(LM_OTS.lm_ots_validate_signature(publicKey, sig, ms, false));
+
+            //  Vandalise signature
+            {
+
+                byte[] vandalisedSignature = sig.GetEncoded(); // Arrays.clone(sig);
+                vandalisedSignature[256] ^= 1; // Single bit error
+                Assert.False(LM_OTS.lm_ots_validate_signature(publicKey, LMOtsSignature.GetInstance(vandalisedSignature), ms, false));
+            }
+
+            // Vandalise public key.
+            {
+                byte[] vandalisedPubKey = Arrays.Clone(publicKey.GetEncoded());
+                vandalisedPubKey[50] ^= 1;
+                Assert.False(LM_OTS.lm_ots_validate_signature(LMOtsPublicKey.GetInstance(vandalisedPubKey), sig, ms, false));
+            }
+            
+            //
+            // check incorrect alg type is detected.
+            //
+            try
+            {
+                byte[] vandalisedPubKey = Arrays.Clone(publicKey.GetEncoded());
+                vandalisedPubKey[3] += 1;
+                LM_OTS.lm_ots_validate_signature(LMOtsPublicKey.GetInstance(vandalisedPubKey), sig, ms, false);
+                Assert.True(false, "Must fail as public key type not match signature type.");
+            }
+            catch (LMSException ex)
+            {
+                Assert.True(ex.Message.Contains("public key and signature ots types do not match"));
+            }
+            
+        }
+
+        [Test]
+        public void TestLMS()
+        {
+            byte[] msg = Hex.Decode("54686520656e756d65726174696f6e20\n" +
+                                    "696e2074686520436f6e737469747574\n" +
+                                    "696f6e2c206f66206365727461696e20\n" +
+                                    "7269676874732c207368616c6c206e6f\n" +
+                                    "7420626520636f6e7374727565642074\n" +
+                                    "6f2064656e79206f7220646973706172\n" +
+                                    "616765206f7468657273207265746169\n" +
+                                    "6e6564206279207468652070656f706c\n" +
+                                    "652e0a");
+
+            byte[] seed = Hex.Decode("a1c4696e2608035a886100d05cd99945eb3370731884a8235e2fb3d4d71f2547");
+            int level = 1;
+            LMSPrivateKeyParameters lmsPrivateKey = LMS.GenerateKeys(
+                LMSigParameters.GetParametersForType(5),
+                LMOtsParameters.GetParametersForType(4),
+                level, Hex.Decode("215f83b7ccb9acbcd08db97b0d04dc2b"), seed);
+            
+            LMSPublicKeyParameters publicKey = lmsPrivateKey.GetPublicKey();
+
+            lmsPrivateKey.ExtractKeyShard(3);
+
+            LMSSignature signature = LMS.GenerateSign(lmsPrivateKey, msg);
+            Assert.True(LMS.VerifySignature(publicKey, signature, msg));
+
+            // Serialize / Deserialize
+            Assert.True(LMS.VerifySignature(
+                LMSPublicKeyParameters.GetInstance(publicKey.GetEncoded()),
+                LMSSignature.GetInstance(signature.GetEncoded()), msg));
+
+            //
+            // Vandalise signature.
+            //
+            {
+                byte[] bustedSig = Arrays.Clone(signature.GetEncoded());
+                bustedSig[100] ^= 1;
+                Assert.False(LMS.VerifySignature(publicKey, LMSSignature.GetInstance(bustedSig), msg));
+            }
+
+            //
+            // Vandalise message
+            //
+            {
+                byte[] msg2 = Arrays.Clone(msg);
+                msg2[10] ^= 1;
+                Assert.False(LMS.VerifySignature(publicKey, signature, msg2));
+            }
+        }
+        
+        [Test]
+        public void TestContextSingleUse()
+        {
+            LMOtsParameters parameter = LMOtsParameters.sha256_n32_w4;
+
+            byte[] seed = Hex.Decode("558b8966c48ae9cb898b423c83443aae014a72f1b1ab5cc85cf1d892903b5439");
+            byte[] I = Hex.Decode("d08fabd4a2091ff0a8cb4ed834e74534");
+
+            LMOtsPrivateKey privateKey = new LMOtsPrivateKey(parameter, I, 0, seed);
+            LMOtsPublicKey publicKey = LM_OTS.lms_ots_generatePublicKey(privateKey);
+
+            byte[] ms = new byte[32];
+                for (int t = 0; t < ms.Length; t++)
+            {
+                ms[t] = (byte)t;
+            }
+
+            LMSContext ctx = privateKey.GetSignatureContext(null, null);
+
+            ctx.BlockUpdate(ms, 0, ms.Length);
+
+            LMOtsSignature sig = LM_OTS.lm_ots_generate_signature(privateKey, ctx.GetQ(), ctx.C);
+            Assert.True(LM_OTS.lm_ots_validate_signature(publicKey, sig, ms, false));
+
+            try
+            {
+                ctx.Update((byte)1);
+                Assert.Fail("Digest reuse after signature taken.");
+            }
+            catch (NullReferenceException npe)
+            {
+                Assert.True(true);
+            }
+
+        }
+            
+        public void Main(string[] args)
+        {
+            TestCoefFunc();
+            TestPrivateKeyRound();
+            TestLMS();
+            TestContextSingleUse();
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/lms/LMSVectorUtils.cs b/crypto/test/src/pqc/crypto/lms/LMSVectorUtils.cs
new file mode 100644
index 000000000..821a86aba
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/lms/LMSVectorUtils.cs
@@ -0,0 +1,38 @@
+using System.IO;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Lms
+{
+    public class LMSVectorUtils
+    {
+        public static byte[] ExtractPrefixedBytes(string vectorFromRFC)
+        {
+
+            MemoryStream bos = new MemoryStream();
+            byte[] hexByte;
+            foreach (string line in vectorFromRFC.Split('\n'))
+            {
+                int start = line.IndexOf('$');
+                if (start > -1)
+                {
+                    ++start;
+                    int end = line.IndexOf('#');
+                    string hex;
+                    if (end < 0)
+                    {
+                        hex = line.Substring(start).Trim();
+                    }
+                    else
+                    {
+                        hex = line.Substring(start, end - start).Trim();
+                    }
+
+                    hexByte = Hex.Decode(hex);
+                    bos.Write(hexByte, 0, hexByte.Length);
+                }
+            }
+            return bos.ToArray();
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/lms/TypeTests.cs b/crypto/test/src/pqc/crypto/lms/TypeTests.cs
new file mode 100644
index 000000000..17229585b
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/lms/TypeTests.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using NUnit.Framework;
+using Org.BouncyCastle.Pqc.Crypto.Lms;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Lms
+{
+
+    [TestFixture]
+    public class TypeTests
+    {
+
+        /**
+        * Get instance methods are expected to return the instance passed to them if it is the same type.
+        *
+        * @throws Exception
+        */
+        [Test]
+
+        public void TestTypeForType()
+        {
+            LMSSignature dummySig = new LMSSignature(0, null, null, null);
+
+            //todo
+            // {
+            //     Object o = new HSSPrivateKeyParameters(0,
+            //         new ArrayList(new LMSPrivateKeyParameters(LMSigParameters.lms_sha256_n32_h5, null, 0, null, 0, new byte[32])),
+            //         new ArrayList(dummySig), 1, 2);
+            //     Assert.True(o.Equals(HSSPrivateKeyParameters.GetInstance(o)));
+            // }
+
+            {
+                Object o = new HSSPublicKeyParameters(0, null);
+                Assert.True(o.Equals(HSSPublicKeyParameters.GetInstance(o)));
+            }
+
+            {
+                Object o = new HSSSignature(0, null, null);
+                Assert.True(o.Equals(HSSSignature.GetInstance(o, 0)));
+            }
+
+            {
+                Object o = new LMOtsPublicKey(null, null, 0, null);
+                Assert.True(o.Equals(LMOtsPublicKey.GetInstance(o)));
+            }
+
+            {
+                Object o = new LMOtsSignature(null, null, null);
+                Assert.True(o.Equals(LMOtsSignature.GetInstance(o)));
+            }
+
+            {
+                Object o = new LMSPrivateKeyParameters(LMSigParameters.lms_sha256_n32_h5, null, 0, null, 0, null);
+                Assert.True(o.Equals(LMSPrivateKeyParameters.GetInstance(o)));
+            }
+
+            {
+                Object o = new LMSPublicKeyParameters(null, null, null, null);
+                Assert.True(o.Equals(LMSPublicKeyParameters.GetInstance(o)));
+            }
+
+            {
+                Object o = new LMSSignature(0, null, null, null);
+                Assert.True(o.Equals(LMSSignature.GetInstance(o)));
+            }
+
+        }
+    }
+}
diff --git a/crypto/test/src/pqc/crypto/test/CmceVectorTest.cs b/crypto/test/src/pqc/crypto/test/CmceVectorTest.cs
new file mode 100644
index 000000000..821bd5a77
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/CmceVectorTest.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Cmce;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+using PrivateKeyFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.PrivateKeyFactory;
+using PrivateKeyInfoFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.PrivateKeyInfoFactory;
+using PublicKeyFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.PublicKeyFactory;
+using SubjectPublicKeyInfoFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.SubjectPublicKeyInfoFactory;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class CmceVectorTest
+    {
+        [Test]
+        public void TestParameters()
+        {
+            Assert.AreEqual(128, CmceParameters.mceliece348864r3.DefaultKeySize);
+            Assert.AreEqual(128, CmceParameters.mceliece348864fr3.DefaultKeySize);
+            Assert.AreEqual(192, CmceParameters.mceliece460896r3.DefaultKeySize);
+            Assert.AreEqual(192, CmceParameters.mceliece460896fr3.DefaultKeySize);
+            Assert.AreEqual(256, CmceParameters.mceliece6688128r3.DefaultKeySize);
+            Assert.AreEqual(256, CmceParameters.mceliece6688128fr3.DefaultKeySize);
+            Assert.AreEqual(256, CmceParameters.mceliece6960119r3.DefaultKeySize);
+            Assert.AreEqual(256, CmceParameters.mceliece6960119fr3.DefaultKeySize);
+            Assert.AreEqual(256, CmceParameters.mceliece8192128r3.DefaultKeySize);
+            Assert.AreEqual(256, CmceParameters.mceliece8192128fr3.DefaultKeySize);
+        }
+        
+        [Test]
+        public void TestVectors()
+        {
+
+            //todo change to project property
+            // bool full = System.GetProperty("test.full", "false").equals("true");
+            bool full = false;
+            
+            string[] files;
+            if (full)
+            {
+                files = new []{
+                    "3488-64-cmce.txt",
+                    "3488-64-f-cmce.txt",
+                    "4608-96-cmce.txt",
+                    "4608-96-f-cmce.txt",
+                    "6688-128-cmce.txt",
+                    "6688-128-f-cmce.txt",
+                    "6960-119-cmce.txt",
+                    "6960-119-f-cmce.txt",
+                    "8192-128-cmce.txt",
+                    "8192-128-f-cmce.txt"
+                };
+            }
+            else
+            {
+                files = new []{
+                    "3488-64-cmce.txt",
+                    "3488-64-f-cmce.txt",
+                };
+            }
+
+            CmceParameters[] parameters = {
+                CmceParameters.mceliece348864r3,
+                CmceParameters.mceliece348864fr3,
+                CmceParameters.mceliece460896r3,
+                CmceParameters.mceliece460896fr3,
+                CmceParameters.mceliece6688128r3,
+                CmceParameters.mceliece6688128fr3,
+                CmceParameters.mceliece6960119r3,
+                CmceParameters.mceliece6960119fr3,
+                CmceParameters.mceliece8192128r3,
+                CmceParameters.mceliece8192128fr3
+            };
+
+            for (int fileIndex = 0; fileIndex != files.Length; fileIndex++)
+            {
+                string name = files[fileIndex];
+                Console.Write($"testing: {name}");
+                StreamReader src = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.cmce." + name));
+                // BufferedReader bin = new BufferedReader(new InputStreamReader(src));
+
+                string line = null;
+                Dictionary<string, string> buf = new Dictionary<string, string>();
+                // Random rnd = new Random(System.currentTimeMillis());
+                while ((line = src.ReadLine()) != null)
+                {
+                    line = line.Trim();
+
+                    if (line.StartsWith("#"))
+                    {
+                        continue;
+                    }
+                    if (line.Length == 0)
+                    {
+                        if (buf.Count > 0)
+                        {
+                            string count = buf["count"];
+                            if (!"0".Equals(count))
+                            {
+                                // randomly skip tests after zero.
+                                // if (rnd.nextBoolean())
+                                // {
+                                //     continue;
+                                // }
+                            }
+                            Console.Write($"test case: {count}\n");
+                            byte[] seed = Hex.Decode(buf["seed"]); // seed for Cmce secure random
+                            byte[] pk = Hex.Decode(buf["pk"]);     // public key
+                            byte[] sk = Hex.Decode(buf["sk"]);     // private key
+                            byte[] ct = Hex.Decode(buf["ct"]);     // ciphertext
+                            byte[] ss = Hex.Decode(buf["ss"]);     // session key
+
+                            NistSecureRandom random = new NistSecureRandom(seed, null);
+                            CmceParameters Cmceparameters = parameters[fileIndex];
+
+                            CmceKeyPairGenerator kpGen = new CmceKeyPairGenerator();
+                            CmceKeyGenerationParameters genParam = new CmceKeyGenerationParameters(random, Cmceparameters);
+                            //
+                            // Generate keys and test.
+                            //
+                            kpGen.Init(genParam);
+                            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+                            CmcePublicKeyParameters pubParams = (CmcePublicKeyParameters)PublicKeyFactory.CreateKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo((CmcePublicKeyParameters)kp.Public));
+                            CmcePrivateKeyParameters privParams = (CmcePrivateKeyParameters)PrivateKeyFactory.CreateKey(PrivateKeyInfoFactory.CreatePrivateKeyInfo((CmcePrivateKeyParameters)kp.Private));
+
+                            Assert.True(Arrays.AreEqual(pk, pubParams.PublicKey), name + " " + count + ": public key");
+                            Assert.True(Arrays.AreEqual(sk, privParams.PrivateKey), name + " " + count + ": secret key");
+
+                            // KEM Enc
+                            CmceKemGenerator CmceEncCipher = new CmceKemGenerator(random);
+                            ISecretWithEncapsulation secWenc = CmceEncCipher.GenerateEncapsulated(pubParams, 256);
+                            byte[] generated_cipher_text = secWenc.GetEncapsulation();
+                            Assert.True(Arrays.AreEqual(ct, generated_cipher_text), name + " " + count + ": kem_enc cipher text");
+                            byte[] secret = secWenc.GetSecret();
+                            Assert.True(Arrays.AreEqual(ss, secret), name + " " + count + ": kem_enc key");
+
+                            // KEM Dec
+                            CmceKemExtractor CmceDecCipher = new CmceKemExtractor(privParams);
+
+                            byte[] dec_key = CmceDecCipher.ExtractSecret(generated_cipher_text, 256);
+
+                            Assert.True(Arrays.AreEqual(dec_key, ss), name + " " + count + ": kem_dec ss");
+                            Assert.True(Arrays.AreEqual(dec_key, secret), name + " " + count + ": kem_dec key");
+                        }
+                        buf.Clear();
+
+                        continue;
+                    }
+
+                    int a = line.IndexOf('=');
+                    if (a > -1)
+                    {
+                        buf[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
+                    }
+
+
+                }
+                Console.Write("testing successful!");
+            }
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/FrodoVectorTest.cs b/crypto/test/src/pqc/crypto/test/FrodoVectorTest.cs
new file mode 100644
index 000000000..90498ae7f
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/FrodoVectorTest.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Frodo;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class FrodoVectorTest
+    {
+        [Test]
+        public void TestParameters()
+        {
+            
+            FrodoParameters[] parameters = {
+                    FrodoParameters.frodokem19888r3,
+                    FrodoParameters.frodokem19888shaker3,
+                    FrodoParameters.frodokem31296r3,
+                    FrodoParameters.frodokem31296shaker3,
+                    FrodoParameters.frodokem43088r3,
+                    FrodoParameters.frodokem43088shaker3
+            };
+
+            Assert.AreEqual(64, FrodoParameters.frodokem19888r3.DefaultKeySize);
+            Assert.AreEqual(64, FrodoParameters.frodokem19888shaker3.DefaultKeySize);
+            Assert.AreEqual(96, FrodoParameters.frodokem31296r3.DefaultKeySize);
+            Assert.AreEqual(96, FrodoParameters.frodokem31296shaker3.DefaultKeySize);
+            Assert.AreEqual(128, FrodoParameters.frodokem43088r3.DefaultKeySize);
+            Assert.AreEqual(128, FrodoParameters.frodokem43088shaker3.DefaultKeySize);
+        }
+        
+        [Test]
+        public void TestVectors()
+        {
+            // bool full = System.getProperty("test.full", "false").equals("true");
+            bool full = false;
+
+            string[] files;
+            FrodoParameters[] parameters;
+            if (full)
+            {
+                files = new []{
+                    "PQCkemKAT_19888.rsp",
+                    "PQCkemKAT_31296.rsp",
+                    "PQCkemKAT_43088.rsp",
+                    "PQCkemKAT_19888_shake.rsp",
+                    "PQCkemKAT_31296_shake.rsp",
+                    "PQCkemKAT_43088_shake.rsp"
+                };
+
+                parameters = new []{
+                    FrodoParameters.frodokem19888r3,
+                    FrodoParameters.frodokem31296r3,
+                    FrodoParameters.frodokem43088r3,
+                    FrodoParameters.frodokem19888shaker3,
+                    FrodoParameters.frodokem31296shaker3,
+                    FrodoParameters.frodokem43088shaker3
+                };
+            }
+            else
+            {
+                files = new[]{
+                    "PQCkemKAT_19888.rsp",
+                    "PQCkemKAT_19888_shake.rsp",
+                };
+
+                parameters = new[]{
+                    FrodoParameters.frodokem19888r3,
+                    FrodoParameters.frodokem19888shaker3,
+                };
+            }
+            for (int fileIndex = 0; fileIndex != files.Length; fileIndex++)
+            {
+                String name = files[fileIndex];
+                Console.Write($"testing: {name}\n");
+                StreamReader src = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.frodo." + name));
+
+                String line = null;
+                Dictionary<String, String> buf = new Dictionary<string, string>();
+                // Random rnd = new Random(System.currentTimeMillis());
+                while ((line = src.ReadLine()) != null)
+                {
+                    line = line.Trim();
+
+                    if (line.StartsWith("#"))
+                    {
+                        continue;
+                    }
+                    if (line.Length == 0)
+                    {
+                        if (buf.Count > 0)
+                        {
+                            String count = buf["count"];
+                            if (!"0".Equals(count))
+                            {
+                                // randomly skip tests after zero.
+                                // if (rnd.nextBoolean())
+                                // {
+                                //     continue;
+                                // }
+                            }
+                            Console.Write($"test case: {count}");
+
+                            byte[] seed = Hex.Decode(buf["seed"]); // seed for nist secure random
+                            byte[] pk = Hex.Decode(buf["pk"]);     // public key
+                            byte[] sk = Hex.Decode(buf["sk"]);     // private key
+                            byte[] ct = Hex.Decode(buf["ct"]);     // ciphertext
+                            byte[] ss = Hex.Decode(buf["ss"]);     // session key
+
+                            NistSecureRandom random = new NistSecureRandom(seed, null);
+                            FrodoParameters frodoParameters = parameters[fileIndex];
+
+                            FrodoKeyPairGenerator kpGen = new FrodoKeyPairGenerator();
+                            FrodoKeyGenerationParameters genParams = new FrodoKeyGenerationParameters(random, frodoParameters);
+                            //
+                            // Generate keys and test.
+                            //
+                            kpGen.Init(genParams);
+                            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+                            FrodoPublicKeyParameters pubParams = (FrodoPublicKeyParameters) kp.Public;
+                            FrodoPrivateKeyParameters privParams = (FrodoPrivateKeyParameters) kp.Private;
+
+                            Assert.True(Arrays.AreEqual(pk, pubParams.PublicKey), $"{name} {count} : public key");
+                            Assert.True( Arrays.AreEqual(sk, privParams.PrivateKey),$"{name} {count} : secret key");
+
+                            // kem_enc
+                            FrodoKEMGenerator frodoEncCipher = new FrodoKEMGenerator(random);
+                            ISecretWithEncapsulation secWenc = frodoEncCipher.GenerateEncapsulated(pubParams);
+                            byte[] generated_cipher_text = secWenc.GetEncapsulation();
+                            Assert.True(Arrays.AreEqual(ct, generated_cipher_text), name + " " + count + ": kem_enc cipher text");
+                            byte[] secret = secWenc.GetSecret();
+                            Assert.True( Arrays.AreEqual(ss, secret), name + " " + count + ": kem_enc key");
+
+                            // kem_dec
+                            FrodoKEMExtractor frodoDecCipher = new FrodoKEMExtractor(privParams);
+
+                            byte[] dec_key = frodoDecCipher.ExtractSecret(generated_cipher_text);
+
+                            Assert.True(Arrays.AreEqual(dec_key, ss), $"{name} {count}: kem_dec ss");
+                            Assert.True(Arrays.AreEqual(dec_key, secret),$"{name} {count}: kem_dec key");
+                        }
+                        buf.Clear();
+
+                        continue;
+                    }
+
+                    int a = line.IndexOf("=");
+                    if (a > -1)
+                    {
+                        buf[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
+                    }
+                }
+                Console.Write("testing successful!");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/HSSTest.cs b/crypto/test/src/pqc/crypto/test/HSSTest.cs
new file mode 100644
index 000000000..25a5fff3f
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/HSSTest.cs
@@ -0,0 +1,146 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Lms;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class HSSTest
+    {
+        [Test]
+		public void TestKeyGenAndSign()
+        {
+            byte[] msg = Strings.ToByteArray("Hello, world!");
+            IAsymmetricCipherKeyPairGenerator kpGen = new HSSKeyPairGenerator();
+
+            kpGen.Init(new HSSKeyGenerationParameters(
+                new LMSParameters[]{
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)
+                }, new SecureRandom()));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            HSSSigner signer = new HSSSigner();
+
+            signer.Init(true, kp.Private);
+
+            byte[] sig = signer.GenerateSignature(msg);
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+        }
+
+        [Test]
+		public void TestKeyGenAndUsage()
+        {
+            byte[] msg = Strings.ToByteArray("Hello, world!");
+            IAsymmetricCipherKeyPairGenerator kpGen = new HSSKeyPairGenerator();
+
+            kpGen.Init(new HSSKeyGenerationParameters(
+                new LMSParameters[]{
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)
+                }, new SecureRandom()));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            HSSPrivateKeyParameters privKey = (HSSPrivateKeyParameters)kp.Private;
+
+            HSSPublicKeyParameters pubKey = (HSSPublicKeyParameters)kp.Public;
+            
+            LMSParameters lmsParam = pubKey.GetLmsPublicKey().GetLmsParameters();
+
+            Assert.AreEqual(LMSigParameters.lms_sha256_n32_h5, lmsParam.GetLmSigParam());
+            Assert.AreEqual(LMOtsParameters.sha256_n32_w4, lmsParam.GetLmotsParam());
+
+            HSSSigner signer = new HSSSigner();
+
+            signer.Init(true, privKey);
+
+            Assert.AreEqual(1024, privKey.GetUsagesRemaining());
+            Assert.AreEqual(2, privKey.GetLmsParameters().Length);
+
+            for (int i = 1; i <= 1024; i++)
+            {
+                signer.GenerateSignature(msg);
+
+                Assert.AreEqual(i, privKey.GetIndex());
+                Assert.AreEqual(1024 - i, privKey.GetUsagesRemaining());
+            }
+        }
+
+		[Test]
+		public void TestKeyGenAndSignTwoSigsWithShard()
+        {
+            byte[] msg1 = Strings.ToByteArray("Hello, world!");
+            byte[] msg2 = Strings.ToByteArray("Now is the time");
+
+            IAsymmetricCipherKeyPairGenerator kpGen = new HSSKeyPairGenerator();
+
+            kpGen.Init(new HSSKeyGenerationParameters(
+                new LMSParameters[]{
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4),
+                    new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)
+                }, new SecureRandom()));
+            
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            HSSPrivateKeyParameters privKey = ((HSSPrivateKeyParameters)kp.Private).ExtractKeyShard(2);
+
+            Assert.AreEqual(2, ((HSSPrivateKeyParameters)kp.Private).GetIndex());
+
+            HSSSigner signer = new HSSSigner();
+
+            Assert.AreEqual(0, privKey.GetIndex());
+
+            signer.Init(true, privKey);
+
+            byte[] sig1 = signer.GenerateSignature(msg1);
+
+            Assert.AreEqual(1, privKey.GetIndex());
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg1, sig1));
+
+            signer.Init(true, privKey);
+
+            byte[] sig = signer.GenerateSignature(msg2);
+
+            Assert.AreEqual(2, privKey.GetIndex());
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg2, sig));
+
+            try
+            {
+                sig = signer.GenerateSignature(msg2);
+                Assert.Fail("no exception");
+            }
+            catch (Exception e)
+            {
+                Assert.AreEqual("hss private key shard is exhausted", e.Message);
+            }
+
+            signer.Init(true, ((HSSPrivateKeyParameters)kp.Private));
+
+            sig = signer.GenerateSignature(msg1);
+
+            Assert.AreEqual(3, ((HSSPrivateKeyParameters)kp.Private).GetIndex());
+
+            Assert.False(Arrays.AreEqual(sig1, sig));
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg1, sig1));
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/LMSTest.cs b/crypto/test/src/pqc/crypto/test/LMSTest.cs
new file mode 100644
index 000000000..7d9cc6da5
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/LMSTest.cs
@@ -0,0 +1,113 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Lms;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+using PrivateKeyFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.PrivateKeyFactory;
+using PrivateKeyInfoFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.PrivateKeyInfoFactory;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class LMSTest
+    {
+        [Test]
+        public void TestKeyGenAndSign()
+        {
+            byte[] msg = Strings.ToByteArray("Hello, world!");
+            IAsymmetricCipherKeyPairGenerator kpGen = new LMSKeyPairGenerator();
+
+            kpGen.Init(new LMSKeyGenerationParameters(
+                new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4), new SecureRandom()));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            LMSSigner signer = new LMSSigner();
+
+            signer.Init(true, kp.Private);
+
+            byte[] sig = signer.GenerateSignature(msg);
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+        }
+
+        [Test]
+        public void TestKeyGenAndSignTwoSigsWithShard()
+        {
+            byte[] msg1 = Strings.ToByteArray("Hello, world!");
+            byte[] msg2 = Strings.ToByteArray("Now is the time");
+
+            IAsymmetricCipherKeyPairGenerator kpGen = new LMSKeyPairGenerator();
+
+            kpGen.Init(new LMSKeyGenerationParameters(
+                new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4), new SecureRandom()));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            LMSPrivateKeyParameters privKey = ((LMSPrivateKeyParameters)kp.Private).ExtractKeyShard(2);
+
+            Assert.AreEqual(2, ((LMSPrivateKeyParameters)kp.Private).GetIndex());
+
+            LMSSigner signer = new LMSSigner();
+
+            Assert.AreEqual(2, privKey.GetUsagesRemaining());
+            Assert.AreEqual(0, privKey.GetIndex());
+
+            signer.Init(true, privKey);
+
+            byte[] sig1 = signer.GenerateSignature(msg1);
+
+            Assert.AreEqual(1, privKey.GetIndex());
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg1, sig1));
+
+            signer.Init(true, privKey);
+
+            byte[] sig = signer.GenerateSignature(msg2);
+
+            Assert.AreEqual(2, privKey.GetIndex());
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg2, sig));
+
+            try
+            {
+                sig = signer.GenerateSignature(msg2);
+                Assert.Fail("no exception");
+            }
+            catch (Exception e)
+            {
+                Assert.AreEqual("ots private key exhausted", e.Message);
+            }
+
+            signer.Init(true, ((LMSPrivateKeyParameters)kp.Private));
+
+            sig = signer.GenerateSignature(msg1);
+
+            Assert.AreEqual(3, ((LMSPrivateKeyParameters)kp.Private).GetIndex());
+
+            Assert.False(Arrays.AreEqual(sig1, sig));
+
+            signer.Init(false, kp.Public);
+
+            Assert.True(signer.VerifySignature(msg1, sig1));
+
+            PrivateKeyInfo pInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(kp.Private);//TODO
+            AsymmetricKeyParameter pKey = PrivateKeyFactory.CreateKey(pInfo.GetEncoded());
+
+            signer.Init(false, ((LMSPrivateKeyParameters)pKey).GetPublicKey());
+
+            Assert.True(signer.VerifySignature(msg1, sig1));
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/NistSecureRandom.cs b/crypto/test/src/pqc/crypto/test/NistSecureRandom.cs
new file mode 100644
index 000000000..0ca8dd990
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/NistSecureRandom.cs
@@ -0,0 +1,192 @@
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    public class NistSecureRandom
+        : SecureRandom
+    {
+        
+        private byte[] seed;
+        private byte[] personalization;
+        private byte[] key;
+        private byte[] v;
+        int reseed_counuter = 1;
+
+        /// <summary>
+        /// Return a seeded FixedSecureRandom representing the result of processing a
+        /// CMCE test seed with the CMCE RandomNumberGenerator.
+        /// </summary>
+        /// <param name="seed"> original CMCE seed</param>
+        /// <param name="strength"> bit-strength of the RNG required.</param>
+        /// <returns> a FixedSecureRandom containing the correct amount of seed material for use with Java.</returns>
+        /// 
+        public static FixedSecureRandom GetFixed(byte[] seed, int strength)
+        {
+            return GetFixed(seed,null, strength, strength / 8, strength / 8);
+        }
+
+        public static FixedSecureRandom GetFixed(byte[] seed, byte[] personalization, int strength, int discard, int size)
+        {
+            NistSecureRandom cmceRNG = new NistSecureRandom(seed, personalization);
+            cmceRNG.Init(strength);
+            byte[] burn = new byte[discard];
+            cmceRNG.NextBytes(burn);
+            if (discard != size)
+            {
+                burn = new byte[size];
+            }
+            cmceRNG.NextBytes(burn);
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(burn)};
+            return new FixedSecureRandom(source);
+        }
+
+
+        public static FixedSecureRandom GetFixedNoDiscard(byte[] seed, int strength)
+        {
+            NistSecureRandom cmceRNG = new NistSecureRandom(seed, null);
+            cmceRNG.Init(strength);
+            byte[] burn = new byte[strength / 8];
+            cmceRNG.NextBytes(burn);
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(burn)};
+            return new FixedSecureRandom(source);
+        }
+
+        public NistSecureRandom(byte[] seed, byte[] personalization)
+        {
+            this.seed = seed;
+            this.personalization = personalization;
+            Init(256);
+        }
+
+
+        private void Init(int strength)
+        {
+            randombytes_init(seed, personalization, strength);
+            reseed_counuter = 1;
+        }
+
+        public override void NextBytes(byte[] x)
+        {
+            byte[] block = new byte[16];
+            int i = 0;
+
+            int xlen = x.Length;
+
+            while (xlen > 0)
+            {
+                for (int j = 15; j >= 0; j--)
+                {
+                    if ((v[j] & 0xFF) == 0xff)
+                    {
+                        v[j] = 0x00;
+                    }
+                    else
+                    {
+                        v[j]++;
+                        break;
+                    }
+                }
+
+                AES256_ECB(key, v, block, 0);
+
+                if (xlen > 15)
+                {
+                    Array.Copy(block, 0, x, i, block.Length);
+                    i += 16;
+                    xlen -= 16;
+                }
+                else
+                {
+                    Array.Copy(block, 0, x, i, xlen);
+                    xlen = 0;
+                }
+            }
+
+            AES256_CTR_DRBG_Update(null, key, v);
+            reseed_counuter++;
+        }
+
+
+        private void AES256_ECB(byte[] key, byte[] ctr, byte[] buffer, int startPosition)
+        {
+            try
+            {
+                IBufferedCipher cipher = CipherUtilities.GetCipher("AES/ECB/NoPadding");
+                cipher.Init(true, ParameterUtilities.CreateKeyParameter("AES", key));
+
+                cipher.DoFinal(ctr, 0, ctr.Length, buffer, startPosition);
+            }
+            catch (Exception ex)
+            {
+                Console.Write(ex.StackTrace);
+            }
+        }
+
+
+        private void AES256_CTR_DRBG_Update(byte[] entropy_input, byte[] key, byte[] v)
+        {
+
+            byte[] tmp = new byte[48];
+
+            for (int i = 0; i < 3; i++)
+            {
+                //increment V
+                for (int j = 15; j >= 0; j--)
+                {
+                    if ((v[j] & 0xFF) == 0xff)
+                    {
+                        v[j] = 0x00;
+                    }
+                    else
+                    {
+                        v[j]++;
+                        break;
+                    }
+                }
+
+                AES256_ECB(key, v, tmp, 16 * i);
+            }
+
+            if (entropy_input != null)
+            {
+                for (int i = 0; i < 48; i++)
+                {
+                    tmp[i] ^= entropy_input[i];
+                }
+            }
+
+            Array.Copy(tmp, 0, key, 0, key.Length);
+            Array.Copy(tmp, 32, v, 0, v.Length);
+
+
+        }
+
+
+        private void randombytes_init(byte[] entropyInput, byte[] personalization, int strength)
+        {
+            byte[] seedMaterial = new byte[48];
+
+            Array.Copy(entropyInput, 0, seedMaterial, 0, seedMaterial.Length);
+            if (personalization != null)
+            {
+                for (int i = 0; i < 48; i++)
+                {
+                    seedMaterial[i] ^= personalization[i];
+                }
+            }
+
+            key = new byte[32];
+            v = new byte[16];
+
+
+            AES256_CTR_DRBG_Update(seedMaterial, key, v);
+
+            reseed_counuter = 1;
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs b/crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs
new file mode 100644
index 000000000..37ac3b2cd
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Picnic;
+using Org.BouncyCastle.Pqc.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class PicnicVectorTest
+    {
+        [Test]
+        public void TestVectors()
+        {
+        // bool full = System.getProperty("test.full", "false").equals("true");
+        bool full = false;
+        string[] files;
+        PicnicParameters[] parameters;
+        if (full)
+        {
+            files = new []{
+                    "picnicl1fs.rsp",
+                    "picnicl1ur.rsp",
+                    "picnicl3fs.rsp",
+                    "picnicl3ur.rsp",
+                    "picnicl5fs.rsp",
+                    "picnicl5ur.rsp",
+                    "picnic3l1.rsp",
+                    "picnic3l3.rsp",
+                    "picnic3l5.rsp",
+                    "picnicl1full.rsp",
+                    "picnicl3full.rsp",
+                    "picnicl5full.rsp",
+
+            };
+            parameters = new []{
+                    PicnicParameters.picnicl1fs,
+                    PicnicParameters.picnicl1ur,
+                    PicnicParameters.picnicl3fs,
+                    PicnicParameters.picnicl3ur,
+                    PicnicParameters.picnicl5fs,
+                    PicnicParameters.picnicl5ur,
+                    PicnicParameters.picnic3l1,
+                    PicnicParameters.picnic3l3,
+                    PicnicParameters.picnic3l5,
+                    PicnicParameters.picnicl1full,
+                    PicnicParameters.picnicl3full,
+                    PicnicParameters.picnicl5full
+            };
+        }
+        else
+        {
+            files = new []{
+                    "picnicl1fs.rsp",
+                    "picnic3l1.rsp",
+                    "picnicl3ur.rsp",
+                    "picnicl1full.rsp",
+            };
+            parameters = new PicnicParameters[]{
+                    PicnicParameters.picnicl1fs,
+                    PicnicParameters.picnic3l1,
+                    PicnicParameters.picnicl3ur,
+                    PicnicParameters.picnicl1full,
+            };
+        }
+
+
+        for (int fileIndex = 0; fileIndex != files.Length; fileIndex++)
+        {
+            String name = files[fileIndex];
+            Console.Write("testing: " + name);
+            StreamReader src = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.picnic." + name));
+
+            String line = null;
+            Dictionary<String, String> buf = new Dictionary<string, string>();
+            // Random rnd = new Random();
+            while ((line = src.ReadLine()) != null)
+            {
+                line = line.Trim();
+
+                if (line.StartsWith("#"))
+                {
+                    continue;
+                }
+                if (line.Length == 0)
+                {
+                    if (buf.Count > 0)
+                    {
+                        String count = buf["count"];
+                        if (!"0".Equals(count))
+                        {
+                            // randomly skip tests after zero.
+                            // if (rnd.NextDouble())
+                            // {
+                            //     continue;
+                            // }
+                        }
+                        Console.Write($"test case: {count}\n");
+                        byte[] seed = Hex.Decode(buf["seed"]);      // seed for picnic secure random
+                        int mlen = Int32.Parse(buf["mlen"]);   // message length
+                        byte[] msg = Hex.Decode(buf["msg"]);        // message
+                        byte[] pk = Hex.Decode(buf["pk"]);          // public key
+                        byte[] sk = Hex.Decode(buf["sk"]);          // private key
+                        int smlen = Int32.Parse(buf["smlen"]); // signature length
+                        byte[] sigExpected = Hex.Decode(buf["sm"]);          // signature
+
+//                        System.out.println("message: " + Hex.toHexString(msg));
+                        NistSecureRandom random = new NistSecureRandom(seed, null);
+                        PicnicParameters picnicParameters = parameters[fileIndex];
+
+
+                        PicnicKeyPairGenerator kpGen = new PicnicKeyPairGenerator();
+                        PicnicKeyGenerationParameters genParams = new PicnicKeyGenerationParameters(random, picnicParameters);
+                        //
+                        // Generate keys and test.
+                        //
+                        kpGen.Init(genParams);
+                        AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+
+                        PicnicPublicKeyParameters pubParams = (PicnicPublicKeyParameters) PublicKeyFactory.CreateKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public));
+                        PicnicPrivateKeyParameters privParams = (PicnicPrivateKeyParameters) PrivateKeyFactory.CreateKey(PrivateKeyInfoFactory.CreatePrivateKeyInfo(kp.Private));
+
+//                        System.out.println("pk = " + Hex.toHexString(pubParams.getEncoded()).toUpperCase());
+//                        System.out.println("sk = " + Hex.toHexString(privParams.getEncoded()).toUpperCase());
+
+                        Assert.True(Arrays.AreEqual(pk, pubParams.GetEncoded()), name + " " + count + ": public key");
+                        Assert.True(Arrays.AreEqual(sk, privParams.GetEncoded()), name + " " + count + ": secret key");
+
+
+                        //
+                        // Signature test
+                        //
+                        PicnicSigner signer = new PicnicSigner(random);
+
+                        signer.Init(true, privParams);
+
+                        byte[] sigGenerated = signer.GenerateSignature(msg);
+
+                        // Console.WriteLine("expected:\t" + Hex.ToHexString(sigExpected));
+                        // Console.WriteLine("generated:\t" + Hex.ToHexString(sigGenerated));
+
+                        Assert.True(smlen == sigGenerated.Length, name + " " + count + ": signature length");
+
+                        signer.Init(false, pubParams);
+
+                        Assert.True(signer.VerifySignature(msg, sigGenerated), (name + " " + count + ": signature verify"));
+                        Assert.True(Arrays.AreEqual(sigExpected, sigGenerated), name + " " + count + ": signature gen match");
+
+                    }
+                    buf.Clear();
+
+                    continue;
+                }
+
+                int a = line.IndexOf('=');
+                if (a > -1)
+                {
+                    buf[line.Substring(0, a).Trim()] =  line.Substring(a + 1).Trim();
+                }
+            }
+            Console.Write("testing successful!");
+        }
+    }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/SaberVectorTest.cs b/crypto/test/src/pqc/crypto/test/SaberVectorTest.cs
new file mode 100644
index 000000000..73bbfdc4f
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/SaberVectorTest.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Saber;
+using Org.BouncyCastle.Pqc.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class SaberVectorTest
+    {
+        [Test]
+        public void TestParamaters()
+        {
+            SABERParameters[] parameters = {
+                    SABERParameters.lightsaberkem128r3,
+                    SABERParameters.saberkem128r3,
+                    SABERParameters.firesaberkem128r3,
+                    SABERParameters.lightsaberkem192r3,
+                    SABERParameters.saberkem192r3,
+                    SABERParameters.firesaberkem192r3,
+                    SABERParameters.lightsaberkem256r3,
+                    SABERParameters.saberkem256r3,
+                    SABERParameters.firesaberkem256r3,
+                };
+
+            Assert.AreEqual(128, SABERParameters.lightsaberkem128r3.GetDefaultKeySize());
+            Assert.AreEqual(128, SABERParameters.saberkem128r3.GetDefaultKeySize());
+            Assert.AreEqual(128, SABERParameters.firesaberkem128r3.GetDefaultKeySize());
+            Assert.AreEqual(192, SABERParameters.lightsaberkem192r3.GetDefaultKeySize());
+            Assert.AreEqual(192, SABERParameters.saberkem192r3.GetDefaultKeySize());
+            Assert.AreEqual(192, SABERParameters.firesaberkem192r3.GetDefaultKeySize());
+            Assert.AreEqual(256, SABERParameters.lightsaberkem256r3.GetDefaultKeySize());
+            Assert.AreEqual(256, SABERParameters.saberkem256r3.GetDefaultKeySize());
+            Assert.AreEqual(256, SABERParameters.firesaberkem256r3.GetDefaultKeySize());
+        }
+
+        [Test]
+        public void TestVectors()
+        {
+
+            SABERParameters[] saberParameters = 
+            {
+                SABERParameters.lightsaberkem256r3,
+                SABERParameters.saberkem256r3,
+                SABERParameters.firesaberkem256r3,
+            };
+            String[] files = 
+            {
+                "lightsaber.rsp",
+                "saber.rsp",
+                "firesaber.rsp"
+            };
+
+            for (int fileIndex = 0; fileIndex != files.Length; fileIndex++)
+            {
+                String name = files[fileIndex];
+                StreamReader src = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.saber." + name));
+
+
+                String line = null;
+                Dictionary<string, string> buf = new Dictionary<string, string>();
+                while ((line = src.ReadLine()) != null)
+                {
+                    line = line.Trim();
+
+                    if (line.StartsWith("#"))
+                    {
+                        continue;
+                    }
+
+                    if (line.Length == 0)
+                    {
+                        if (buf.Count > 0)
+                        {
+                            String count = buf["count"];
+
+                            byte[] seed = Hex.Decode(buf["seed"]); // seed for SABER secure random
+                            byte[] pk = Hex.Decode(buf["pk"]); // public key
+                            byte[] sk = Hex.Decode(buf["sk"]); // private key
+                            byte[] ct = Hex.Decode(buf["ct"]); // ciphertext
+                            byte[] ss = Hex.Decode(buf["ss"]); // session key
+
+                            NistSecureRandom random = new NistSecureRandom(seed, null);
+                            SABERParameters parameters = saberParameters[fileIndex];
+
+                            SABERKeyPairGenerator kpGen = new SABERKeyPairGenerator();
+                            SABERKeyGenerationParameters
+                                genParam = new SABERKeyGenerationParameters(random, parameters);
+                            //
+                            // Generate keys and test.
+                            //
+                            kpGen.Init(genParam);
+                            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+                            SABERPublicKeyParameters pubParams =
+                                (SABERPublicKeyParameters) PublicKeyFactory.CreateKey(
+                                    SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(
+                                        (SABERPublicKeyParameters) kp.Public));
+                            SABERPrivateKeyParameters privParams =
+                                (SABERPrivateKeyParameters) PrivateKeyFactory.CreateKey(
+                                    PrivateKeyInfoFactory.CreatePrivateKeyInfo((SABERPrivateKeyParameters) kp.Private));
+
+
+                            Assert.True(Arrays.AreEqual(pk, pubParams.PublicKey), name + " " + count + ": public key");
+                            Assert.True(Arrays.AreEqual(sk, privParams.GetPrivateKey()), name + " " + count + ": secret key");
+
+                            // KEM Enc
+                            SABERKEMGenerator SABEREncCipher = new SABERKEMGenerator(random);
+                            ISecretWithEncapsulation secWenc = SABEREncCipher.GenerateEncapsulated(pubParams);
+                            byte[] generated_cipher_text = secWenc.GetEncapsulation();
+                            Assert.True(Arrays.AreEqual(ct, generated_cipher_text), name + " " + count + ": kem_enc cipher text");
+                            byte[] secret = secWenc.GetSecret();
+                            Assert.True(Arrays.AreEqual(ss, secret), name + " " + count + ": kem_enc key");
+
+                            // KEM Dec
+                            SABERKEMExtractor SABERDecCipher = new SABERKEMExtractor(privParams);
+
+                            byte[] dec_key = SABERDecCipher.ExtractSecret(generated_cipher_text);
+
+                            Assert.True(Arrays.AreEqual(dec_key, ss), name + " " + count + ": kem_dec ss");
+                            Assert.True(Arrays.AreEqual(dec_key, secret), name + " " + count + ": kem_dec key");
+                        }
+
+                        buf.Clear();
+
+                        continue;
+                    }
+
+                    int a = line.IndexOf("=");
+                    if (a > -1)
+                    {
+                        buf[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/SphincsPlusTest.cs b/crypto/test/src/pqc/crypto/test/SphincsPlusTest.cs
new file mode 100644
index 000000000..a76602697
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/SphincsPlusTest.cs
@@ -0,0 +1,448 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Pqc.Crypto.SphincsPlus;
+using Org.BouncyCastle.Pqc.Crypto.Utilities;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+using PrivateKeyFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.PrivateKeyFactory;
+using PublicKeyFactory = Org.BouncyCastle.Pqc.Crypto.Utilities.PublicKeyFactory;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class SphincsPlusTest
+    {
+        [Test]
+        public void TestVectors()
+        {
+
+            // bool full = System.GetProperty("test.full","false").equals("true");
+            bool full = false;
+
+            string files = "sha256-128f-robust.rsp sha256-192f-robust.rsp sha256-256f-robust.rsp shake256-128f-robust.rsp shake256-192f-robust.rsp" +
+            " shake256-256f-robust.rsp sha256-128f-simple.rsp sha256-192f-simple.rsp sha256-256f-simple.rsp shake256-128f-simple.rsp" +
+            " shake256-192f-simple.rsp shake256-256f-simple.rsp sha256-128s-robust.rsp sha256-192s-robust.rsp sha256-256s-robust.rsp" +
+            " shake256-128s-robust.rsp shake256-192s-robust.rsp shake256-256s-robust.rsp sha256-128s-simple.rsp sha256-192s-simple.rsp" +
+            " sha256-256s-simple.rsp shake256-128s-simple.rsp shake256-192s-simple.rsp shake256-256s-simple.rsp";
+
+
+            string[] fileList = splitOn(files, ' ');
+            for (int i = 0; i != fileList.Length; i++)
+            {
+                string name = fileList[i];
+                if ( full || name.Contains("-128s-") || name.Contains("-128f-"))
+                {
+                    StreamReader src = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.sphincsplus.subset_" + name));
+                    
+                    // BufferedReader bin = new BufferedReader(new InputStreamReader(src));
+
+                    string line = null;
+                    Dictionary<string, string> buf = new Dictionary<string, string>();
+                    while ((line = src.ReadLine()) != null)
+                    {
+
+                        line = line.Trim();
+
+                        if (line.StartsWith("#"))
+                        {
+                            continue;
+                        }
+                        if (line.Length == 0)
+                        {
+                            if (buf.Count > 0)
+                            {
+                                string count = buf["count"];
+                                byte[] sk = Hex.Decode(buf["sk"]);
+                                byte[] pk = Hex.Decode(buf["pk"]);
+                                byte[] msg = Hex.Decode(buf["msg"]);
+                                byte[] sigExpected = Hex.Decode(buf["sm"]);
+                                byte[] oprR = Hex.Decode(buf["optr"]);
+
+                                SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+
+                                FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(sk)};
+                                SecureRandom random = new FixedSecureRandom(source);
+
+                                SPHINCSPlusParameters parameters;
+
+                                string[] nameParts = splitOn(name, '-');
+                                bool sha256 = nameParts[0].Equals("sha256");
+                                bool shake256 = nameParts[0].Equals("shake256");
+                                int size = Int32.Parse(nameParts[1].Substring(0, 3));
+                                bool fast = nameParts[1].EndsWith("f");
+                                bool slow = nameParts[1].EndsWith("s");
+                                bool simple = nameParts[2].Equals("simple.rsp");
+                                bool robust = nameParts[2].Equals("robust.rsp");
+
+                                StringBuilder b = new StringBuilder();
+                                if (sha256)
+                                {
+                                    b.Append("sha256");
+                                }
+                                else if (shake256)
+                                {
+                                    b.Append("shake256");
+                                }
+                                else
+                                {
+                                    throw new ArgumentException("unknown digest");
+                                }
+
+                                b.Append("_");
+                                b.Append(size);
+
+                                if (fast)
+                                {
+                                    b.Append("f");
+                                }
+                                else if (slow)
+                                {
+                                    b.Append("s");
+                                }
+                                else
+                                {
+                                    throw new ArgumentException("unknown speed");
+                                }
+
+                                if (robust)
+                                {
+                                    // nothing.
+                                }
+                                else if (simple)
+                                {
+                                    b.Append("_simple");
+                                }
+                                else
+                                {
+                                    throw new ArgumentException("unknown complexity");
+                                }
+
+                                parameters = (SPHINCSPlusParameters)typeof(SPHINCSPlusParameters).GetField(b.ToString()).GetValue(null);//todo unsure
+
+                                //
+                                // Generate keys and test.
+                                //
+                                kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, parameters));
+                                AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+                                SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+                                SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+                                Assert.True(Arrays.AreEqual(Arrays.Concatenate(pubParams.GetParameters().GetEncoded(), pk), pubParams.GetEncoded()), name + " " + count + ": public key");
+                                Assert.True(Arrays.AreEqual(Arrays.Concatenate(privParams.GetParameters().GetEncoded(), sk), privParams.GetEncoded()), name + " " + count + ": secret key");
+
+                                //
+                                // Signature test
+                                //
+
+                                SPHINCSPlusSigner signer = new SPHINCSPlusSigner();
+
+                                FixedSecureRandom.Source[] s1 = {new FixedSecureRandom.Source(oprR)};
+                                signer.Init(true, new ParametersWithRandom(privParams, new FixedSecureRandom(s1)));
+
+                                byte[] sigGenerated = signer.GenerateSignature(msg);
+                                byte[] attachedSig = Arrays.Concatenate(sigGenerated, msg);
+
+
+                                signer.Init(false, pubParams);
+
+                                Assert.True(signer.VerifySignature(msg, sigGenerated), name + " " + count + ": signature verify");
+                                Assert.True(Arrays.AreEqual(sigExpected, attachedSig), name + " " + count + ": signature gen match");
+                            }
+                            buf.Clear();
+
+                            continue;
+                        }
+
+                        int a = line.IndexOf("=");
+                        if (a > -1)
+                        {
+                            buf[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
+                        }
+                    }
+                    src.Close();
+                }
+            }
+        }
+        
+        [Test]
+        public void TestBasicKeyGeneration()
+        {
+            SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(Hex.Decode("7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A3E784CCB7EBCDCFD45542B7F6AF778742E0F4479175084AA488B3B74340678AAD111491E7E52F6F1D726DAF2A4E75CAFB60D034B6E912B26BE68464B0095D60D"))};
+            
+                FixedSecureRandom random = new FixedSecureRandom(source);
+                
+            kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, SPHINCSPlusParameters.shake256_256f));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+            SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(pubParams.GetParameters().GetEncoded(), Hex.Decode("3e784ccb7ebcdcfd45542b7f6af778742e0f4479175084aa488b3b74340678aa8edb4e5af38c3478ef9533585e368ab0689227f536cc71d8f0affb01e19751ec")), pubParams.GetEncoded()));
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(privParams.GetParameters().GetEncoded(), Hex.Decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2db505d7cfad1b497499323c8686325e4792f267aafa3f87ca60d01cb54f29202a3e784ccb7ebcdcfd45542b7f6af778742e0f4479175084aa488b3b74340678aa8edb4e5af38c3478ef9533585e368ab0689227f536cc71d8f0affb01e19751ec")), privParams.GetEncoded()));
+
+            SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubParams);
+            PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privParams);
+
+            pubParams = (SPHINCSPlusPublicKeyParameters)PublicKeyFactory.CreateKey(pubInfo.GetEncoded());
+            privParams = (SPHINCSPlusPrivateKeyParameters)PrivateKeyFactory.CreateKey(privInfo.GetEncoded());
+
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(pubParams.GetParameters().GetEncoded(), Hex.Decode("3e784ccb7ebcdcfd45542b7f6af778742e0f4479175084aa488b3b74340678aa8edb4e5af38c3478ef9533585e368ab0689227f536cc71d8f0affb01e19751ec")), pubParams.GetEncoded()));
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(privParams.GetParameters().GetEncoded(), Hex.Decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2db505d7cfad1b497499323c8686325e4792f267aafa3f87ca60d01cb54f29202a3e784ccb7ebcdcfd45542b7f6af778742e0f4479175084aa488b3b74340678aa8edb4e5af38c3478ef9533585e368ab0689227f536cc71d8f0affb01e19751ec")), privParams.GetEncoded()));
+        }
+        
+        [Test]
+        public void TestBasicKeyImportSimpleSign()
+        {
+            SPHINCSPlusPublicKeyParameters pubParams = new SPHINCSPlusPublicKeyParameters(SPHINCSPlusParameters.sha256_128f, Hex.Decode("b505d7cfad1b497499323c8686325e4711e95f8a383854ba16a5dd3e25ff71d3"));
+            SPHINCSPlusPrivateKeyParameters privParams = new SPHINCSPlusPrivateKeyParameters(SPHINCSPlusParameters.sha256_128f, Hex.Decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2db505d7cfad1b497499323c8686325e4711e95f8a383854ba16a5dd3e25ff71d3"));
+
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(pubParams.GetParameters().GetEncoded(), Hex.Decode("b505d7cfad1b497499323c8686325e4711e95f8a383854ba16a5dd3e25ff71d3")), pubParams.GetEncoded()));
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(privParams.GetParameters().GetEncoded(), Hex.Decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2db505d7cfad1b497499323c8686325e4711e95f8a383854ba16a5dd3e25ff71d3")), privParams.GetEncoded()));
+
+            byte[] msg = Hex.Decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+            SPHINCSPlusSigner signer = new SPHINCSPlusSigner();
+
+            FixedSecureRandom.Source[] source1 =
+                {new FixedSecureRandom.Source(Hex.Decode("33b3c07507e4201748494d832b6ee2a6"))};
+                FixedSecureRandom fr = new FixedSecureRandom(source1);
+            signer.Init(true, new ParametersWithRandom(privParams, fr));
+
+            byte[] sig = signer.GenerateSignature(msg);
+            byte[] attachedSig = Arrays.Concatenate(sig, msg);
+
+            byte[] expected = Hex.Decode("B77B5397031E67EB585DBA86B10B710B3A36F9C5CC3A2224371A11E36D833073B666C5A7692DAA400171A5BCCB0EC99CF2F67B0CECCE24F4538A20E1671E8DAEF86CD5F153AF0EFD5363FE52C9855777214702E497AD64163C347181607B1B3710175AE1A4B168EA194E8D844D55D3D0CEE10CCB310BF4BDED9103202907D1AD5071672F3EADDBB70EB09FD2FD2A0A9793ADF79DE0731D56B9C5270E9F02777ECCC9129B34F2FA7B78AD28DA674C95BE47D4547ACC18F5E3CD0EDF42C51513DDF11D55A1E97D944800BE90E550AF9555022F0CF64B56F3E9A769ECBAD8BDBC18C4A686B76CCD3EFE19D122C89E65F22E70F1364ED0CA95E7DC9A598F6CF1C5C2D1A9DF83E3A339B845FF7B0B24C695378B995312A3BCC8221957E9FD19DEB2681E03556C50055D5F242A7640791FCBEC36EE50860FEFFCD2EADDB7E01AEFE0A13293AB583D13BB441BBA77248FA235A652BA690432395DDEA999E2136E61328F732FB0EC93029214937834A75AF922DA4047D552AFB53386F6509603E81FF1AFF7DD6DD39C770DEC1FCF7E792157800B13057162EEE5EDEE94A8D92432019B8B918154B1F062ADBCC4EE799A7AE87C327A22A4AEE41E1DF8AC0B8E037415AD70090E974B2C7D1890B0FE8B3C57E311D41A8B8B3ADA94B03FA9CA53561CAD470AACF97DF0C49B14F903E4DC6D2B1B9188D361739A0055AAF14CAFB671E17B28FEF6ACDC760D5BEEF07D45A999DD40BD55F37DCCCCA84A9972E88442BA10BD726A0C3FCCAB7235DA8D9D7821F2C4AF1F76EA52C8D7EE5FE4F81B454283A770BB9CA7E49EE08FF9BA86D6A82DBC637A1F16D1AD07132ED8858644C33FC7FDE8CCBD751B221B7960536A6B10924227622A25BF7DFB3793638E5F09EA819138C9D8451C4C00395F30B83C2CB9D314DAE6D468D4BBA5D70F29CB95A5843D7AD3954D0427861B063D2CAE3D8E1C4D38555E48DB82C81FC08FD94D8E9D473CD8DB8EF96E3B88F8AC30A124FAB8C9738194C560EC2D973AC55E06A95EC92F66C3F00D420355C5E8883DA9089D58D0E49D1E3D9B96A39AA444C73E954DC88FE53DBAFE22EC91300A0728321766112C04B55477AB9C52F4682DE3E8F53A32B452CD0F87DAC836352C2677BA97CA1C591DABEADBEA61CE191CC30B3CA0F0976F2E93287DB7C0CF1506826D48BC5579DACB6FAA404F57FCCD6F26728642D77DD0874DC7CCB9E9F45A7D36C0C2C1C483ADAB69CF445E56FC5D44ABA2AEE4F6BE772D330FBEB9FFD853E3C0A06F165271DE04A743D924E4DF03B48EAD67FF458968D876B7486A25DA048909C892E858995F433E791D86ECCAAD1B2C1F6AAC92D994A26D24E960A90B5537C3C679DC46FD714E191A50E2798F794659AF8CE73059565899E26088FB0F98567528E81142A46B36853100D378FF00AA389A435AF92A3472BD20F0A00FE1A553E4744B6D4515A945FE1811C9F7B9637BEACFDBABBE36CACF31803EE41A8020852DBB1BD67EF8AA003F06D6EB371B1DD4024E4347F9381FACF4F7F0CADE7C6A6586A4AF1F9A240FDB3AE3CDC54906F7677D10E3A83B31B6F48B993A5AA07C6C68D2A30A3F8DFE3434C548745ADDA40C01AE58CECB64866A52C422A214CF65D243EE3F716240D1C76C066DE94F44387A14D26C1E6190EB259856D4326535541C7D526A0E4A5A80DF931C118532518C321B7F1FD22784D0738F9C986752700019E41B789729F30C4013FBC69199D61139D9BD8CB686691ED11ECD5025D1E6123FF9240D2A2475FE4B1957214AE7EE930010AA25CB19D619734F8BA013D5895AADA9C5DCC9E0283EBDE787C3C004C0AD4C120C0D5C68F5BB9C346436EFC7D40B772231CDAAD2BA813F705B778F2B77787B85876120A36CF837A70045FDF47AB84A136787214EC0D5EBDF685D6154CBA24B5945AF73C7410B80DFA607A478923F5DCF4008FEF72C17BF0CA115C559A43774A9E2B72D629DE3D7610EACBAC220327A0F095A883244AAF1F8D901B935912C2D03D27A00BACD9F2E66C6FB6E1ABEDE95CE9C23D13E38EE6FF8FF32DE2EB370CED90F863916C7652FA24C0E558004B128468EC24D4871C072D2058F51729EEB73F0D2A9FD7C835853B7027913ACEEDE6A0F1E4552DB98724438ADC72C5706162856F5479063004A1F0BC9F92442B6273D2562E8584850BBE47143F1E1161179470C59A2D520537FE289C87FC30651757A71AD650043E5617F7E256AAFFB8B6539F59CE30B1E1DD585DECDA0E1C0B16A27768BC0529B593F76AEB89DF6F6E7411A8AA2FDB11F9040FB5AA21396C2B993D1F7F5A11185A6066878B2781B477AEB43C040B4837D1AAE8AB0CFFB5CDB7D08CE197FD01CE766B9CD0C0BA99926233112C54F50605F64CE35FB4553857914164F67F44A27EC6507F0A572721BFC95935E1D374A9E3738FA83F3AC8F07357917ADE08812D1A8E75031644CF67F34686A8E2643D04C23250B96E1AF93630488AC996C25707C1877E1C49A60B6924EE88D7FDFC7C8A98AD91623EEC661D834DB8089C411C254F2D5A8B77CAB4857C578F62F17F38A1F30D7C6B3E8F69422AD4D86D08DD55AFD8FDCCD9C4942595A1DB0F86A8689AC33A1DF9A508417BC74106686CDAE0E405799707D438BA39F135BBAFA35D252B9CBFDEF0CEFD98B4A0AFDFD460EA7B8C384BC1CE5AEE8C1C1A16449237994779CB88F1416E910A4B37FC0E719BFC555E494CBF05661EA206EFC53B6D97C15C99A99EC42F045295C4303CB0CFD8A5944CEA384D5AFA8ACECECE264DC13BB2542CC74A52D7608FB53C9E4D4F27ABE820C01B9FC42939B2B4481CA5FBF9EFA5FD2662FCD53ED21063F2CCD4154828D8C0A8E9AF2710521527B866FEB6ADBDAF9B2C53323999552AB300DA1F605FCF5EC4B92BE5A8442D89268E022D99648E9176F975240E98C97DA91E04AD59883560261051C2CA9DFF8FCE8D65158841BF7F07A5CC93A405A8F14720F2B250FF6FF64C08BEC145202402DD611E99722BC5EF8652F748BA0595C83F788E73FE17472C284587B8AEF53581454C5E850F8D389B56C4F7BF7AD6EAA9FA0288DA9BD7A3B10416806D02AF6901EE29EB910E7024C65D10E1494C349C8C9E4E7E20A9B80479612EF652E8365541482F6A47AEFB4C89F6464AE12439158761E2CC0572E20CB0A5BF24715A237B0C3448A9EA51DED0E805FABBE7F6997FC12C8A65A5B575F499624BA52A25CA79C032605CEF3E485F587DD4B283D20727969459AFD8B89245A012F6202A7C2490D9A2B11FB749F855FA20481E9077E1DB86FFCA49720C1F2C6C65B9744CB9CED93621AB30E37ADE5BC1AB6B4E67E84020604A3116D440345126F487A211778592592B18C295E57D3FC1DECAE620338221A607BD1E3B89632B0BEB929256A4A4F178CAB98C7F280BABF826784D50CD724FF6FE7B173D452579DFC460536D35E2F765BE6BA17EE9B84F25EA2CC96C0F6332ECF593893261D3A062B071B1075CC2132F04D45A8A445729081120E3F36DCA6C7DBE655A907687D985D3F3477E84D8A81EF63D2D05607CFC70AB7A3DDBB1199EE5EEC811E23C29B8A68FC47BE95F8E9FCB36AAC17263A08B4B2A33436C39D6EC5136A4C3CFBA19ABA3D1C4C418230037D358BEAC6A826EAEAF095699D5E086F9BD0FDD70B34E8F07A3292A296788F5A909BE23E3748CCBDACFCF056B14B6A79108C17DB0A704B544713CA9BE09F4FF3732F4B30C8C91E23CABCED533917E53A3933D7C70EBF519A5EF6E54212D7A8A9F018AAFB8AA40FD335243C96D484A9B2A156B04F779A5BC6986148AAA31F925533B9078F0EF2B2E5224035639CF2E49CA836CDCE8B03413D10F5E730CA546331ED2DCA21674FE9242F0CD0CA38D8537E07BB9DCCF44F0628BBA9B47B89849AD7063DC7FA3E98689FCC4B656BCDD46F15B061B5E05E987B2DC9A2041CD008B1F17D53ED38B4447F5B9DD9A9C5A588A5D603C625FC29D3BBEADA79A9F6A41335F3DABEB07D73F4331CB05161D241B84A92382F09594B5BA616C13CDC597C9DF0AABDB6C6F5571213E8EECDF0E4273CA170F07F3D255CEB00C837DA16EBBB03919DBEA3BB55476AC1B9F1BEC97A1A4E51E6CD7FB5B29598258089B37AD37D4FFF3F618114F355A2CE77F07E439FE3F2E14C2E6E705B2F5F148A3C7805545E4D7C4E50E08A4200DD6E0D9E21347E01E8F45D7B1787777E1373A6EC22A50C1C010F394EBCFBA36D28B1D822307CC7DCFA13AF8C61C63C4744137827BFB8F9F0A01CCFD7C539CCFC9EE10E6AC0441ACC14FF443D0E2BF361498118451AFCA362496E7B62737045E434012AD91E426FE97A98D0269703486CFD5DEAAFD00C659CE6870C8852C3C6BBA83B6B9F3FBCFA0E02BCC18754624E6598A8D1DE90CA931607D03D4602D0AAED25942A27A28EE1E281264937F4A7C785B7B744DA2601798B48B1618FB2486662585849B4F743346B672B973DAF29961E04399CB339A4A71FD044627DDC68FCAAAAA978F5A8B67625D7761E37B61FFBB6CC327113BE98D383D8FC71CD6FA9A22BB371019D585083B7ACAFA40E96AA88008A9853F137270B9B87B0D6D1E67E9B1FA3ED5A92EF8CE3442AFF5F1BA087CA8421137A69CED2C37E10FB242BF928994391E5A39F40E8095F0A2E85A587093A939F364D0AD8EC1D62C54E2A33DC94FBAE1F09304E159A8E018B74A4000CBA3899FF7DA14098C092DC83546B6A19FB67A6030B43E1F638AA0FCBCB8B3288993167C90DDE4E34FBE0685142DEEBCAFBD8FF5968D356D0C6DFFEA0C0B7D0DDC05B3FD135EA42F66279171F2D4239FFC5BE7403F498A319048F55811C2B63A3A6B631CF96F59BCC689D2890AFAC48C723DE8A66FCD2A65F9AA2AA165EA53511143FF54152E7AB64F96F5E28EC9FD8E73E3B5C7FE7CBBCFCE5BFFCCBD5C53A9F73EFF2F1C95A2848EBBCBBFF76BC0477470FE5C4B106BF97CB88233C795BAB518EBBA0EF31C07DF59EB7DF1C60676E12A667FE85E7B10699588F268679995E3A12DD99C5FF393AC01DF76DEB442D3705A4B0BFBFCA55AEA8D12C7A90D7AAE453FBBB9C23F89F8CE777D4B24DCC21158346EA2BE553CCE385EE53A1488C270175D36117D8F995482BF840200D94F0F1F86523B8F5DB18ABFC4D847E585A75C2AB0FE602B55560A6811FE0C3AC18ED5E64B585B727EAF5A75957872BFDC08166E957944604B0572355313EA82F553A69FD5DE76F3C344E186A164BC950FDE22F5744017DC6EA82FAB13CAAB2BBF71BC38AFC68FED0EFC3B6A683EE45B582E01E6EDF2A5D85497BCBF23E67BF28879B4844C773BFD78C2A8B9C7DBFF3A53C1DA2026C3C5A8A3DB0CF17549F31B307F8EACC7B89CD31D99C0BE707F4F81146F41F46DBC4D3821CD80D002945C752D2C4E6CFD18DC597768F87E572E79A9444BCD241424BD5BEF882683884E3D650DC60EB80137CEE87B2C5C958A1E29AB40FB3B98481FDB8A6A459BEAB371D01C450277852101985A54736311964930BD566832AFD8C8797DEC770B150F71DF54E4B0B4FBCF4453A32270F129A21B347514C4174FBA95F599F730A0F563ECB285484B289677CF0D61AA8B6FCA5EE6B53FEFC6AB97D763E0340158E904A74044E43F429B155772CEE802FD4DE92A1D3F9B4DA34E285D964FC7FBCA79E97C11A715E1CCFF07692027FE2514C5250FB5F9F0569EF563FEBE491C7EB22B586B13244094532EB7A5B06C59714036B457B60868C43F8F3782C01ECC8CCF6BE78D057CC8056907AD2BF83CC8BE7163B1CF43D8AF560102F2D2ECD4295056961316BE161A1A38ADCEA881166AA0E21992D2FD59449D44AC139B3C6CE0D94F1CCF780DB9054ED47D0BC8EB85F53056FA037D73FA895FD8FDE5662ED5BC2D2C428822433721D0BD8BB8CAF50766309DAA92BA7ED1E12DD1FCF02924A9F715C0802990F73628B14D82E5E9929AE2BEC5EA52CA4B79A864CE0F26FFEDFB207FCAAD3DDDDECF8515DEC6A3E96F0C292C880A5D629BE55EC417E34C94536D9FFE75D3C7CBC972CE633941BE63089F2B2EA45E254083E0C170327C7C45DB6764DA773BDF07CDD21B4D1497C0AA4983FDAA4FF2CD755B54C4046DB35FBCC7CA3C2C0BA79651FE2B9D22E6FD7D00302FBDB0F573A5722EB631EC0020A949DB060973BFD0B9B61BEDA1B0B8AB9FCA3DD42CFEC78AF467D0B31B7A78C4F18526BE58CEFDAAD4FBF204AABC1BED430DDDC82785F0BB04EDD4AB0B12190BD2D65899010C8C8E04D4608FB87D5E93122E6B460CE899E35AE09297EE514D10C375559C010363108260538273FB5D980B1C4157CA17223D506E043AA0884E3C1D1CCF80C123C900FA7900E8791EAB8071E5A92516C2941B70371D551A1FF31C9AB9FBD7B20BDEAC222B7D33851B0EACD75C0EBFC5683317E38F136844E5389B685F3CEA2846D02BEE40D5B5BCA365DCEEC43E0B4AD4CD04ADA0A45E75021F3443D2EA11FA8EE882B15F30FA6BDD55F6E44BF16FDE0963EA1A8090751F310759F61FA2028A73DD6149B716C9F11BBD9FD3DF897E3BC7F68EB7E07AAA901355C04D026B6FAC380C9FCF1B273A746208821DB3419BE8C0C38F817254D3EA273960B017FB22CA84578310F0978CF1A8310A5E35569477C9C7AFC1637B8768345DBD56DB2231A5C9EED4DE74F9C0D7C69DC8424EA2D17380F6F23CCA52DA4280AEDBFAC0B9A14CF9767401131E96C5CB78374FB5B0002BB1B50D734C4446FD435B0B941F64138E64FB4C1B488DF098A7AD20E7F5140F99D357E6DEBA57A5A3E1BEECE7CAA9EAAFDEAE26B9DD3A190DCF807D2E9063DE3026F85F5375B0945AD319F47B072348EEED817C22BF126AB28EAB498695428F10497433FCD6B4E7DAE9B6A5AAF4ABF90CD347F0999F1110453A71B55BF0A0A4C8C01413394D4A8AC140099335FFE6269775D0" +
+                "D42E0451C7D2B7F7608CC26A7B10B37608F4C578DA7B54C6ACDFD15A10783B46CB7908E53AE036D4E19A2591359E7AA11B410BCD1512C261FDF94967BE0F447BF0E0F1FDE616ECC70B17282064A0F5A89A93D3EC3ED0C49B66C1B3B2A31800DAD0CD6132D0390729E2003F30571A4BE1FD9FB64C007063004E2BE3677008287D48ADDCD949F920FBF082D8FD4D9A2D45505AE35831B2057C54560F40412F053716270F3AD2385AF6DD243BBDC4B025FCA76932643842BEC9DED4BDC5F30733B2DE487F997F5ADDB7ACCF37E5FEDB9528670401A6698751AFE0E13F1C355E729397A3BABACEFBC174B8AEF4BF80C1712A121462073BB4DA4BBB7C1264CFF119FDC2AAE56B864BA0296ACC39A59A14C82424C55F67DCA9EDB5608B3D627C136EC4E90AE8E811C919E5ED973F8DD8F9A4F6F3DD409282200F35DF3F7425A673F2574321A3161CAA86A9D813AFEF3E4065A50B6CF798732E2D7302578F7F7D56EE05DCAC2ECFF6F79F34039495E0DAC9473773CC9489A06C59732716FB304A4899D743987AB1D175031AC5505EEE949099455903505145B7B7B909C65B9533E64552DEA2A252366702A65DB0783E82C4C18F2CC604F03C6C5050FE365D4645A16356141B5CAA762A5CD20D09EF5E962E3BD6E5DA9F7742F55E32DECF2FE310E17D1F0448C9599C3889ABB9A904AB350DE7137969A2EDA21F2CA54578E077E5E2168DBE56BB290B7500654AAF71E32AA2DA571122401713A8E26F23889B4E437B87523AE9FA2F0F977FC8F1A983260ED74333E1482EB41B0C28DD21DBBD1DE2AAC0F0BF3ABA192A93CBAF6B543FF5AADB529C92DDDE0BCDE66D73B30CBD509C17D5C80C00FBF5FC8004A5A03448619CC2FBB6003D340F262B46A0B70BB6BE687FE676B7B5EF7D0FE51F349A9EF21EA95AADF3A33CAD9802EF46087885EF63FFE269366C536887FE8E8232EB95640716D7B81F71C886205240231D3201886E888C06474D14D0F26E65102A308671D61008B3FEE3E73B96E123E1993CF7012C5A42D17703DF535381042F60C902D92E55FE239CD0030978B8FD65243DF8DF718E9B308E50876D42D2C202C51C238578D803A479154315D18EBDEC4C57D3863D2478AEEE69FB416E8B3A03B44876AB5887C7445095BF5A204A29FB88D1978DAE76F98853CE2AE767584FA0E9A392599E640DBE55A63EE26E21B868F113BC89F3523F27E109B8B6BEAB6D35985A51ADF79A497CC7D5BE5D208CDD0FC75A5722265E77A608C3B91B6A96855FCA82A0C2CEEB28CE9E277F958D4F2AFF664056EB2E58616454D7E02F17DC8B17001F6010027E83F3287B4D49DE0BBB319D9E865E8284D7FD6C68CEE1E7B075EC79BBEB22A65FD5942EE568C505BA5A5E2EDD8E82C28C94643DBDD3FD5F8E13AAE3546A199BABE16F3F6A8E3E356892418594FB61A5ACB5A1C5013DAF610B683150CC4A0134604910F680121EABBD0B6F397BA425797DE3298B82496F7F4062DC5DAFCE7BDEC4991C921AD6E6C5758BEB188C76B88DB3A012091584016B5D161AFE086E4849F201742CD0FFB02803148E2CB262CEC9AFF6448370FCE12DF750DED05805C788FC2AF93C39CA023FBCA8D99BEBA76721BF166183ACAF2E3EC00B7B716EBCBEB73F78E93492FB0415FA3A1CF590FE73741019AD648B9809D94EE647BBEE3382B608A7D33EA73B80D51C44A064E1A6FEC3044C19001A069C403B13F7723D9EA0948AC8372AD82899EA895E6D606EF170AD860C7CD6F9D769A6C78A24450AD5D08A9E2E7D343EA9DD3F5F7B035E8CE3D7D739D30A253B80B17711C71BE014F627DC96E1A223584A687C74ABD8CCFA8925B84D8652793B65EA33316D7CBA930960F4D50C4857B7F367FAEDBFB64E4BDA092008658EEA0241A3387B246E2E042C1AE9A5DBF30CE5DA3A42D775F9B7868B03A5D1A3980386EAB9606652593FFCB7DDEFF03729CB25A736901C026DC2833D57641899D69642E227126EFF37EFB87D6679869D574FB19BB42C64A4549CA1D47BF495489D10A7DA3DF99E0DFD68E177EF8FD92027741FAFE1730AF167C4BA99C8EF0BFC6F96EAEFD756C8E4954B9927787C0C53B1A26EA0851949B928ACC9341241DBA0A6BBDB2F53BA6D57A076CD72F3ABF0128183CFDCE835E2102AE27DE63F717E6EA8F21F90E4D27EAF9F6F92F7B4D1E2C0121731BE18E3EAE2D4A667BDAC3E8956307765E387745DF9A2E40C31110661CE474ABA673E0B18BFD5BD680216D19AA037E5E2D5FC6FB2E311E86DE3505757E081B1B4CA167F4F6AF1A658F3BFF0E8D3A6A4C8E01792091A24E52AF0E5B864677DB3C1EC1600A13EC9ECBF886386B57B4BC2A1EAFC5F50BB2923440338960AC38FCF39306E7C0724E372E8E7629410C839D238ABC18A6DF8D9A79BCA4CF5679BDC2BCB685924BE9CFB8191F2472191990C714995CAAFAAA261A85B272FBFB81E43BA7F5DEBFE489E47E8F19AC572F7F39998A5EA735DA2D30A471CE9DC83174948453E9CAA9B86B00453493669903D81905D3FED65B9447AAA7F38B8949942E0D480582AC3713383460086CCF167BB8995B7A4E033A53C4E3723A4DE73446829927E2CB2A3A1C14F4CFB1AC3F906E11DBFB78140B27899099E534594222C79C3EB9893E40C7274CC50F521E7F24462E346DF963E37303E9DE64C6777A8B91B8516FC7FBA1BAD060ED5CD5C7E672D033B461A882E289F2313A58CF3A88C9082BBD596646D97440D85DE1D798EDA2E74FBABE1967926A07C4444F827EDA8F1238BAA15ADF1DC38307F7AB2E3DC1EF213464CAE3B8B1C4BD37FD50957CDB4E317869813F88EB9378F6149E95D898D661CA3C238C86C180AB40FFDBDD5953A3210660F73D69AB9FA3EA16D25171A38DE4E0AF1FC77BB0C6968D3302DF7261744CB7C3583D581A0D940EE12077988D99050AF06A52EC210C03EC8067CCB046CE641BC48EE407D67618A21F31E767AB24A7DCBFC94DE22E866B4EC7DCBAEDB16107204EC39882D9E823B16DF7999291AA85507FE8F89BEB2AD7DBF68FD728E1A2D791D0E84DA90E52EDCF184EC97F4A1C9FFBBC8611357B274169AC638CE1130D818313950908CB462B261052B4E41D11E1A934057C356EB74F8EACC8B39847511BF4918599A338B134F5040A42588A742271F4DA751A52920C36CFDD89719CB954B7F225D895235034A9DB11EFE7AE74DF329F2CEF61E63D6E20853CC9E604D1572D996728D43B3A3B517FF1E8C2229EAA31329E9860C246BA8D3252A20BAD15271DC8B2ED46BEA7B6B33385C24C590DFA69103E60D4E62D844485ABBD874018F063758EFF0B47AE740AB5524FEDBBFF664F9FB1F54A3339FEB8E86266BEC6388F025F6D7FC7CFC4DD92C069DD4614FC2AFA22C63B9CDA8A01C145D35DE9523F245D4DFFFE37F006501E74D0E492E92A736B272DA4387E0FDCC51EEF861D87C8E7097EACDDBE937562E8FBE8EF78E22F1D78F318C8EAE7D4459EC074FCE5BC56A878FCF7D99E7D973F58BB6A028F53550141B4B1F1AB828FE924EC1606867D4ACACC04226360CFD57A109881D66B6976C4DF6DB21635A372A41ABF49C8AA8694DA3F624B440B88B658FD75DBA26C02B5CA60677C60D28F24AE95BE6A0FA8149EB90AB4CB91C524F6EDF3B6E3ABF20AA04FD52AB47B6EB9C090FA7E6696822451304518073D7E4C173DD3E2D8889F0B21129319D8EC5EDB2523DD6BBC8BD0B1622A3B52CDE42728F4715AE54E62665B7453C144B78935F64EF57D2D0896C07BCA7B8447F4109DFBDA4734E8A2C0CB3A87EECAC77F49FCD30AB7E4B4DE524E53ACA0BED1554E7D13AAB94C14E5B732B0351ADC55B8F82EDD296AB73F25832EF44080C13CCEF5C5F8BA45CB3A5098DB5F9F1B89199AF4B17E3F70A3680FF8B1513C8726E42BED5EC5F79C4F754D98767994D7802B5FE6A91743F2821E1FE79A9441D6CF9A7F285DA22C87690B0BB8C0880EC75382994F5A7F909BF3BAE5233EA9DA1364A4B7C271D4F63AACC504E65BCEBF609FD66DF6A2B598C667CC357A3F6C537DCE76C22B5BA2171EDE56E686EAD003AABA5D7E9C5F595A81379E06B7AFCF0E8B5068B729DD6C697400D81B7579EC3C7314180CEFA6A55648D2B0E4723520CB8E7E7B1F86FA59A751CE27D2AB07AE2753558B52C12247DB6FCAA2CB5E0E6B8F2565C4685F7F9C317081F2007EDF166CA13F99B39B8902ABB6144AEFEDB229A79E1E0658B31C19FEED3B92110FF9666654E4BC4BD653AA3A8B5B8115FEAC5C5637BC7F2A6F27B03EFB53F493AD1B8850C7854FF856515530EA542546A83DAFB2577CAA13BC433EA94931A3A736925C21534D06BA2A089052E8351FBE64510D16CC50130169CAF78E23B64E8242D521D892D4CAE0F22D7EF20B9D4A3B49804C02F2873E23CFD762EC30EBEC27A856F72CEDD9B4DD1605FB7BD59CBB464D4EB4C1CA6D2B5F320C881D00F7F84E6F1D151C67DED339F08DFE751C4D5CB40CD9E8A22A60C9F54F38227A3219C7E10768C6CA931EC8D19999C8FEB52B24674652E398686B64FF71B704194A1FCD66CCA0667E32B41F8FB70F81B18E2080CD6727F92C22ED5039FCEBDB6802DDE8B6529D7AD2EAA94778118428C730C13DCEB154B08E3A58199BC258B460C96A67895F7486FACA5E78078F5E33D89ABDBE0704B74CFADB899FD70924B0203A1B5E16C301C3D3F6CFF1230B6E19757D05B2E80E7671FEF111E71037D5AB289CCA81E0068F57DD8419685F3F752E72D3AF2588D045DBE757188D3D5B8A38640D173FEC10EA01DFB054283E82D2413DF332DABD7E95258685BE57102F03DF01E8F1695F8FD25B134D3D103841357E250B5333075DB34FE5A0C17100588397F7D93AF796D4655A4D6A1AB3B1200ABD5B0AA1A49476CDB8B4B0FE421EC4C39DA555EE74419AB0E8D4589C14E11399DA66AE778859A1C34ADD195E60E271C2D38D6B705330877B84F7C986896356C1B2FE65DEA14E2B9A1FA5E24F649F915C0BB12B5807E8C6FB32E7A4C9783AE702629401C48B53C65DB7E19925FAD7E014E5F2F175AFDFECA3FF66A40C5FB8A07F4711ECF27085A577FF99E3407211E45DAD212ABD7AFD1CAC3D08CE5D5D3735291C75247C018E60A69218BBC3243FEFC3E0ACEF51033E96F2F8A775ADDD75FFDC5CE309C7B68998FA855B4D0267C877798A7CE0DC3BF2A18CCAF9CF167FBE25F665C35459FBC274301D7F258F5435B025C3F3220BB9F1D718854C586C238B6EBBEFB7D92634CECB2375F836F7F567B71D09F595C749AF2F984E08C9DBEA31511492537F74DE9C9B88C3BD33FB273507B956461D3FA630DBF51A6854708728E64D10B3C6CF1746A50A4A0D84FD7A9428AD147A171050FB276C6B6E3A7CAF4781499B31715EFB3B0E5DB33D8A34FB2DA1A77239E99002470A52EBA708DE93A1EA26743F3D8225317F4F88798B906B4458362FF819D487BFC8770418C959FC147C5A729B4EA58BC3CBCD66FEC3BED48389184908005204657DD0C7999B68EE95F60C1CAC93A664DD6A14E79C8FBE13DDC13EEDF6438BD76BD3DA1FF0BE38DD9325A2F7DD6C4585BD4C487DD9357AC15A8732F14AAE84902CC1B8DFB98B26D0C32DFC3238E97D9EFA18667EA5BDEA6E7BA8BADBC76C0F22BBC0BA2FC368EF80AA1DE9EB0263B6635BE0F9435415B7B6B4FE028EA2C57B79897FCC7F208E102C9D4763A0AA10CF03A76B9045FC0AB78FA2AAF7187F236EE21F68D6432A96CF532CA72AC1FCAC9D38864ED36D98AAA00F5035367EF260B984D9BB9FDA9BD4AE02DFC3CEC33BF989F0752C997579A1C6585C876D28125B1CFC4DFB03BBCE397B5E8C49BF5A83D73E04A27001C49F82B820085D44FF353371AC7E1064032D271887CCCA77F4C24FBD836377E6475579CCA23855E0EA39C7F762B00368A3B36AA80AA0D529AEFE0C8FC098AF5911E845E78DE8C11081F2DABD40BE30AAAB48B54D3767B498F6ED95CBE61B79CB1FCC0F8C90923D37C8B7327CF1C5ADF00CB32187357607A2B38F5B66F62D293D91945C9054752B1C82A8793CC1B95F0170F7B8314B4DC954FF68BF17F1C2CBD3859CE3FB64DCB6840E1EA129F07600FBAD0ACF27CEA02103B7FE8E7F1F36FF285686FBBFAC4C774F9B9748A293BCA234221C4BC9E0CD3D97DB56F542D18AE1C9A47951CDBC8722178FA7244DCC7DC56A7810A8F10F9F1C291D95EF29363762524DDEBEE0BD7691F188E1BC412B9289911A91D12C4E6940D3767DB04493C66B7453D5C2D22B79FE36FBBE273FF3A88A8B9F05C30B45BEAC15153F093C4A6B932D1FB00433BF7455F654E7F75AE03E8E5858B0ACF30071C677A0466A15C5BC94A7C46251AD0CAD7E3DC36F6D184379FA8DD357D2A32EA7049BB966215B0640FC01355204D856DAFD52E0B40D82D56B16E9A15663262BDCAB1A0DB97A589A52FEC0C0C4093356601796827A0064FCF85DE67DE7BF5977537EFB0D3DA0D8703269A46D0D3CE65360B8D995CB6C00D126C824C7D56D741C3DDC724EE9E2E250116362E9079B7BF4B0E9CAD71EA8DCCFAE3870E31189F5B80D16E50BEEA4511F4EEAB9DA9DF046EBD001F4038FD7707DD9C31825434AF11075CFED8C2F7EECAC2FB34A48C94D912CE26379FD320E3A0FC38A51E2D6712FA9FA993CB6B34EDB54CE83FB9CCE03E5E55BA2909F4BDDF34F17F33C5768738D91E098BCE919F9C3F74580A6DBB10CAEBA9BCAF6A8CD1FEE79055EF30B786E0D8CA75B7FC876FABB5408BDD95608D12C10FAF035FEF78B4B7E01A3BE71F006CC73F00E399BF1FD42317B77EA2E8754267A8B4DAB9B37E06D9944003FAC1F35EC83936750F0FEBC34E94927B003BDF41FCA881C7618F1A0B9AC900EC0A5C34EA0633B1F8149CF194489C8BD4E1E4F40E2E380C66DD539A3DCB80BF35A0E25D30B175A41E967C7D4FD676DB30D434FAEA802B9B72DB66E701CD3E33B144F87724BFEDD2B0BD92AFC0DE491B973B744B88BB5BDD83537E27483AF04BD5790E29ECEE97876E47A1D200785CE7532D767E1D04C44BC266D201F34CF0F5155A42573892F5CE26E172E4CD4E931A5CEACD2046FF58DD7AC8ED0FAC4737267A69AC52D6BFC870C00DA19509579716DD38127EC7F4B471949CF85ADA6AA75BD9CBA7739428D8AE9FFB3965A728AFD186C0F06C75A6073E6A8F39D6D78E2EBDEABDC6247030B221447BDE32AE8099FB0FC7B6B03E036BE098F25A428266C77AC28B0A16A7CF4A8B22089021F2FC1D98C53889C7A1CF4C98534943A1632A38FA86DA4FFB4252450891338946AA07B58EC5D57347FB6BDF0B5A5D61AFD223FEA965A1E205F0EEA982F36029DEB8FE8AB66DB9E464B17AA416A4D1DFF0A7772AB783F139ADCC116A23093C3BD0D95738F66D8D8BD6CFEBB544E739E162AC34CD2E21009B995E8155ED2964CEA9DEA8B2A09F5FDEDF7F957C1C597474B8EBC721DC61CBB582E0EDD6B01AD3E526177DB0DA9B45A2CD2722488231F55F0665F3166F68D0E25213C8847E9BDC44101EE42CBA6BB40F0096A941AD4C17251393FD1293CD6E66C5B7095F83FE232F446FF2C8DC33F88FA4863A7BD76B7C31F6D1E719352D307AA94B322E8DCE0DB9726059039BFDC4BE015DE7BCDB5CED46C0370B6573DAC66DE7687EA69CDF7570DA816F642108EAD77F4657C0B2AD65DE3CF9EF738C53EDE4A32B3E1EA5568AE3C9A0326AFF7624062CBF3E8511731CBFC991C3C58C517602DC7A812840DF10319A383A42BC268A2B6BEB9CF76EEE82A8B13397311101E8A36BF59A34C9B5FD2B709BD65415409472275CADA1512163B5CA2DD04FF51C53C16B2616DF3C82F87D1104B641977FD7ADB7B7831151D9CC2CE13F2818B01EF8C1CAAF8896A9D00420D913C21459C75CBB56C2010328E0E9743E5F80BA1543F55250C7F454AC805F361965F02B523F08C90D2AF978EEB105B20DB1F463623C56583440C831FA4C592A4D88442BCDD8E012A12F7450E81AE8D3D07418943432DA71F38F90926B67EC5FCE524B064CE3FE44D49A22A87A05DCD247DF95BFFA0525CA4F1160EEA69EED57AC26474BC5397F6BE6EFDC3608C18A121F00E8781C97C70C35610EB0D870E3BAA093DE905158DDDB405F3DCD994A65BEC36B22431B17B5560EC6E3AD2034AD12264E5A3E2EB4D55D520287C5CC57EF3B734BBA81D2B016CE587168430205A77473D3951AF22CBD2578F2B0BC0B7809A07D545B4AA0A06F1E593D8CF34EBED127ADE073CF3CBC000EB63345DED4E64B987FD1D6257523D2136F3843D6CA5233CE23BC6ED48673890B0E879AEB77487A5CEA8875DC9A9CB2EBE755C9E91A202B313F67CFE75BB5CAA42C97ABB31BC8A084289D8976ADE9908401469AC73F2331AA49CE503BDE68FC8F83FBFB3A12847B1079A5ED5912BAAF3970A26E78A94F7A68D14E496D7A75F1BB63864FE444F273E3305C29ED1E00ACDC7BE47A0FC398249D5E63E688FF5BC28488A77EEF13FCE338626E398F590E96D4A9D523F26204326C41876F2DAA11732848F720E954F3773C5A137412517F86F719FF2572E2F7578156EF13D6A35A8893BB333100D1C92A98179773C2520A90ECB1A90A3D610D07EE17A87C0DF5F92212E05E65D28FDA7D1FDD8F649B6A6C1A51084D49C7FAA831DC59519A301952B2BFAC6C9DB95698EE4A9B680FF00F15358956171386A8E1957B00FA51F6DB0E0162702FCD514C9497FE8E0466328C51F070001E0BDD91BC06C2445FC0BE6AA748DA1945722DB9A9034FDB5E8A00D056292453CC8AB2984CD821CB8BA7796AA2AF53EDA34210FC8DAF3165A465F40BA92C90FA96232398ECA4021287A15AB2E1D9F1AAABE1773FDB875EB234A313975A854C36E28308656CE98C087E4872647D1106FC347463F924B291D7F6DBF714FCCF141A9E974AD6CDE14E9CF86061E2CBB40247BEB48C03C2BD916764B265C3DAAACDE35920B745C56E912DD93245C40B38A41B9F7A3D725D7BC4BCFE30FA84BCB0E21F383764A66DF2BCFC1793F75A6D0EE8506CAA606057B52C4F11AE869BD5E19797CD7133A026D915B5B9B3BDC46CCAE4573960FD82A955C516EA0DCC5DB7372496E7656B69C331921F72CAC6A7741B4713B8AFBF29C40E0AA3F7117611D2CF99BF4CB0A95F8B42CA946A7A25BA12FD039BF83D3E446D4618B735DA2BDA1FDDDA89132694A14949789A22FD1E6C6168F365EAA5147205DFEA168B72ED5DC0E1A9A97392D5C1169961E3197FE086DD94FA1A0C171A88A253DF93E1F089C613C723234D496EDA57A1EA6F429BB152AC11901DCED5B1C26FE04D37D4C05DC6B0B1311DE467C9E5FBFB45DBF425EF3B9B0DAE52C56A820E922B2A07F8BAF0CD7E3B8AFA9CF98AB6B4D352A2D6C1F6D8976785A6D05D67F7F2F7F610EB390C1159DA261A092988D8A5E531362BD4269574BE3EEE529F33CEAD0C9B3A719D9D3F7E77E68E5B8899F6242A2771D16D3F0C3F0CE2236C36F4B9CD39FF6EC52013A0553A2AC4F1FC24C22A032A97F81D4DC7905C6D37B746277A22F6FEDEEF43A0E1B80EC877439CD1C1C88CC29ED6170766C70D67A4CC8E4026428088BAA4DD8A907B24DB5AF13FA900BC3D98960FAC305A40AEE6FE35D361A792E5F8DCF580263198C0015DCA966E404931B2942937C11986D6590835E2AD1994600AB73FF82CC50D8CE879839FE23A68E7C517AD72650B4168A95FA436B58EF1DB08BFDA1754A14B31E8ED00CB31AD7D5A48BF70C3F2F2D5A29E8BD06B37F97819DB29106B911949D2BC22C9CABC02B1B1D54BCBAE5368F44D0DF928A4A60692D935E1687B357546A1DA980E3D0C0669F92D99AC73D4CF14801C173387DEEE3507297195D6F988D4B7FFDA4EE067777D07CD9FBABB96A5644D64E16F6E24675C99106E8847BD2415D73FA3E32EAC68CC641BC39726D6A10A2336E95A57B72657A8DA1965A2597B5709E764F3B624CC6ED8C80581D28A7114EDBBE4E969115338CA90557CAF9EA4546192883D3EE2E3A4834C87F9B0E230571773ECCA50F79DA69AF5E7843BC14A8DC5E1CDF279569B2F962D4243900709091BF860D456065BE00C24AB26E3868BF9FEF8712BE33C5BAC3A8120CC9B65C75027C2DEF2DD2850D4EA5A7B0CF58E9228401002A86E00FAC8210B42498D2E1BB8BE1A8CF25D9D896077928691A9A431CA41BF612B5B734043D58FCA8E0B36EDB5EACB2C44E1C309D54AA9156388028B8B16EB778CEBB61A4534A0781714B6AEBDE44A4574C89D4585141CB3938AC381AEB4EC5C9C1E8F9EF408CEF0773F900FB5C3BAF410E34F577CE0E2C45CF6870355FACFD4C25ADABEEA155BDD557C95A969FB9640BC74DE40D6C905ED8E55D3CBCDC907843C4C96BF6B114FA06A18B47A4F2CC8485C6D3EB071012604D85A8E26131331BADF7AEA59E42EF916BB01A159F3FF1ADE933D346E77ACE0107FE082457CA7DD406DBB13E55A6715FB1406A5B3CAB76CE633BAFBA13CBD8E451715634704A350073BE4C55EF99C4739539D55161F8378BD68D68C8C5B9AACE7935FD1AD3444A61B03AA7217F8C328B8359A1EF1A2D51503404B74F5BD3EBAC047C54ADC95B25AA357040EC44499D7E330B434FD38331AB7168A08A13068FBC89FD0B116D6DDFAF7D40ADB617B6108E336AC5F466F7487D7807A887F03F919A3ED00814D1CF3DD51F8086FFA2B96583F88124526ACC1594AD44503B31D81E24446632F9C161BDC104B9C57A6E2BBC986E1C76545912F8723D67766FBE3C94472869ADB5D68D59174B678CD6A0E705CFDDDEB10431D2344A12666AFAD27978DE0E910BD72C78A40D907D40984E6934DB60173F584EFCAB2B64CDB92DDB10194809915AF60BDA815EA4C280EAD2D9D589953B736F4D124BACDDB1678E064A7BF346AD764EE5B315B52A79E8BBB87230E0492C956F140CE0494BA4ED81A75299D9DE1B8E50DAE686937AEC54B953D65424FDBDC0C2A5682EC0E75089F9A9254B361BED3C5C5133F3F6835D905B1BE9A17C4F3B6D322637EF40FECA00894E0187DF8EC701C28B744310726EA9E322CB8D2DD159E090F4BB05F5A49DFC60D1DFA4E4D9EC4DCCF62C61268390E3966E899E87822A687AB1887D7AA4864FF3847BBE27FD93B60D0751748F01B92532BCD3D12CB3A344AD8F4FF29A93906AB3A5EBB485AB565612E041B08DCDD7FD27F4B199122049E8B9B3077A4008EAFD9A6DDAB12472C46FA67FBCD9A4E802BA065287A63096DAB6EB2E690187E3F2F53456A812DEAAFD5D4F99EED0EA80FE32A81D3F3B62279E5141D7B86F8BE13B60F35D98ECFBF1ADC25BFFA1FBB49A2328FD2A3D042C9355C2A0242F7F3F29F8B5971A1CD999ACEEFEA8E4F2258C3CB90A184E0C4EE6ACAD96B7048048DA7FD4DD91B12FA6678A9154CD9376BC9231B9D8FE38344245E1F2808B54E2F5234463C61CC3C16AD68E953E71894F416BA6B73EC727F3612523D9545063FC5FF1813E3467207D00118A02C97086EFD1C3FF9B2DB525B38B68F517834B8EAB55795BC2458FE88C0180C3F85F323C859BD09BA94874BA3843DB15FB73788FDFE02377EE608FB0C0E0900252F3220CAD2E503D070712ACD73559C9FC36853432A9E976051DEAF21AA060F919EFBAFEE3C1F3A2F4C0E693CE635BA92FE4B985CF9F5AB2FADBDF7C14524E120647E6EB0C59A70948714EE35D235A6F3973A60CA43F60BE2F4197B3746F9BFD02849A39819A5533C850E006F897AB198CBDF60866A6C034F8A004B16C7E287874AB383E428DAA4E3595FCB5529FDCB4EFB4D78E93CFF45D16170EEF5EB264E348BC324015BF9CE316E844D66CCFA10116B4725BFAB978F9B6EDFA1843D6956F85BDD747BF4AF96302464E8E2417A5A6E172C4201B9DEE6946B06231777AA5296D1946AF8BC6021DF693FB464A99932DAEAA6B16FBDDBF283D82D9BA269B96D58E6E2531126421E30A33B2634A3BA372BD09B00A1526DCBADE3CA2B3BBEA156E539EADD30E2895D90905788812850497462CF4F9B93AF2C87018097E2E78E749D110B08EB2C3B0D2ECA914854E7F95CC8001134EC3DD9F981AD14E4093A5337609D216F7F69BDFAF16CA8A6C0FF4F5D7FFFACA554F91340E4A018AFBC5C13BC2B58C63764B2E32DD71BC2B9737C73D99B9E87DD5B285EA6013F368424A27EB0C66C2D434C33F1D45746EF0AFB31AAD1546E27B08D7D348463A3C530515AB0594A0A5B5F4FBB8C4993D5B5E21936148FDE77E9EC12CFBE9CFA4285437A4AAFE6BC2B57E9F72CF78E478EDC97D6B8197D96DF528FF0127B93AC157841F1F09454FD4A3DE3F6926953DFE69F5001EFFBAC03CF0A90E1E6D2647A04E675EC37D391B1BC6F1960880FC7BC37C94821B929CA4B78867CDDE7BB94046DAC33E38954E09BEA22480E14899C7FF50C31059CFA00D8C735E85579559751F269842C58996A3F4B3F16361E211BD25B1C82CAAF7B690ADE068D2CE81433B5CAF590263370D0593B81497A2C05EC1E87AA5DE98A353C9E510798610E2D38A81150888BAFD5383CA4A57BD40DA6BB2FF33964545AC39EC71B0E2E43EF82F0FE01B07C49691D0902782886464FB94FBEB9B0576689CFEBB929BB547DB3A42495B91ADD903ED8FBFD5C5B63C1E3734827C1EA84ACA07325139EC055EDD5FEFF8417427321094AD4E761373AA28A1027D3F1786F00F403390DFFC7C318C35FABBB216DAB17AD0A16454C4FACC68A39ABC3A2C09D0773F89716E6B6FD79E84F179053B89211B4CCBB07B6EEF354564CF51F9A823D515790ADDD4D1A82A2756C9D331902F3B1589D82EC0339648EA53DFC89970E83907CD4606F64AF034B15DE2AC5A5E052238B89DC44909ECFF199DC137F146122494E509CE000F549DF2BCCBE153E18280F86AC834493477340A7F0352392E580411DB8B9C63BDA11421BB103468398C3F253EC5BD4F914D5B8B31CCBDFC3C250CA1C65A9733B84D8C631675E50D176ED3A9B3B998BA998A94F03415C79533D3B091F63A538F434E8AAC6CE0F787B3044C298F90589CBE08E933925AD6DF055E881756FCA323234D5858AA438DABFA791AEBBEC34A5DE5705B820E86444E37DD0BDF7F780F1622396C92FEDA628DF606195E331CE6F06B316CB1E578F05C73A6F519DB8EACDA109E405BDEC0C517119B0EC33D8DEFD22889D92AE0BE2E2900E2DE66C0F2165B00C70C50F0E8102E76FFC6B7DD3BAE4D9C449B395E3B0BF0947A9FAECD5D349083B592CF7B85DD49E8CA89134D97BEE12E70CA2D315A4DC1404BBAFE8AA5A4E62D15E399CF6B942E1824D9FAFD37FFDF885ABEB3EA053C1E65CDDAA82574643D956DCD29EE1ECA71A99EB1BFB4E3CCF144DFDC5D13605B84220946E393B5D8C37DBE149959919B9805380A3FE1A22E6EBC7011016E142E73CF45AA0C9261515D741B95455BDBE800F286403CB7087FE511F9A03F5BB8AE5A33F4FEA640F980B648F3FEB0D1FD4709D1217A9C55E9C71AF14958B51C7617AB369B56ADCA25FC1C1574224C7FD5CD1CB6D8D37A6E2C1BF50ABCB70E0632E2268CB7BAEC5E06E2DEA9AF2F985AB3FDECA6B32A8981E14E983E083273C48A74DE5DFEA75A5B3FE474156D7DD7B33D513C1349289EADA8CA96792442F70AE35F5AE232B147225C911720BD8F228A46143CAF1A7556D29C659250E1240BD7F4CAF04E4AF9B7B1DFCAC0EA1BBC34085D5AF8FDA29AE5368479A7928BBB64084C94963F7332B435360BB7B8AB34D41562D4164A219C5D0C3B6FE5D9E7F1F0A83F8FFA835905663A46CC8C9AD04EFD88CD2A1596CE7FA0C66F3396C0FACBAE4A85140A5D40DD0B606ADC28BA94034BE683394F91DC8E42DDDCFA2DF4B3BE3527FA33418225726E80E0E0D6959D3D4A98F48ED161D27473F52E48D2C417CE5F5218CE90F09218CD3BE6323D20ED9ACE88FE3E027C3895FAB3B4ECD8EED7DB2E5A53EEA4E8021E03C58CD527DF7EA8339C6DEDE7D18245F5529248D7969955E829BA58B9625C9877629615DF31CC7E7F2D6933B1B74E27DA5261FC11BC15967977FF7255DFB4770A9A9F0086AA31B8F4E9AB0C61B7DC82A292D622C716A53650DD938CFFF0DF1EEDF94C9699F6394D598E72143EEEC92C58514EFA44C9630A678CD791895945C76854D3DC8D7AB566D4110B9FEA056B78321E17FFFCDDC50D08BBCF9C521EC14EC65F7452A39BDC7D24136FA3E092BDEFB4C803EB741B003591923B079BE485A9A0CCAE1DBA0B0D545FB6A449B5CE44AB522C132A89437095494EA32830879F3EDD8490B6F1FA3AB43E5E121CB14F752978CC47C24A55D8708C752CD50F6E2D5EDF27ADEB3A451B5F233539F44405411ECC05B90E575030DB1BE2F2D142DC617EE4EBB7DB4336909EA79D5C9DF4A864D611A6845F94B7DB542810D3B1D33C11ACC9956F7BE0F2DC3FCA24CAABA71BE5C5B340AF158B1AE1DAB56FE046DE73255B524A1FF1C1F6325349193307B93FC4B476132CF900A838FD00113608ACF5629A2A57B5C62B6505C91945D7493887E15C531FC365ACBAA4422AFF92FACFB0E0890BD0244E37B442F0C15505616542B9ECEA6AB0E129DC37E8C6A477B2F445BB1396314C989FDF7F04710D9E5D34C777EABD9FA51043784760FA458496C5F784F3FC1A3C29947DC8BA20147824350873C17A68C12511FD14B2E181AE65FF9FEA6A6D570756DD1BEF59D74AECE74B21ADA2AA37D633A97082662B725D4C42407D1E25CC90A05A253D8B038E98BAC90996E0772022CBFB4895AA3F6CEFCB1BB44561F828B70C91FC974C175347E8CE0DF0491B0F022DBC878B403B89C3B5A997FC33BED721957C48DD3266F14246F4A2DE8730392FD72C7A40C903664634D826F216E96E06037C1ABC237FD6E8437C0297B2D5D8B371F1452FB9921731864B9C8A018DF753FB1D0B40987D774B4176F712717D6DE7E59232EC5AE620DC6E939DFBE54B67082F1C67C827EADFE16F28DD3D815521165B81A4ECF7F714FF7011A45C82D5EBD583C9E19291E385924A4F00CD298FCC82584D70F762641360C071ED1FA918CD957CE773AFB58E0C72C670BCE39CD0CE6F7D56637CB8B61400FDB3152A5EE16170593E64BA4390A82C5A72D7C30871D98A1368AC4781BED73018E037F9792B21681A91D26EC663FFFA1E646071589355F795F9C8D5A15447B2686266E0B77AD6D928AC130DF9CF2BB3F91CB15C4BC5BC5D5B642845C451404B2AD26FA55BC4367D2802F713386D58A2F63171B17EF58FC93D38594AB35A4015DEE13D592F21B61B0232502268008323B63976BDCE0DBD61F5341AAA7A7E2034D551F989962F5320350C1E0F851E3D9E19B51055EB2E05D6A24E9C819E299C47B29D11F68B77E362873E0417D7B6715269A4A405F8EEA95F8E6F9416D236AA46ABE11AEBF56C4DB1951BDDC5DA439D4B099F215076484F82C0E9A34C34890C2BDB4BBCDAA8648A299C1D4FEFA8341A7FF8054FF309B0FFE0F3D52791E51103470DF8A5B70F6E4430191A281A75C9FAF1AA0B82C85B3BD1E5D38EC499BACAC5DE81E187D974305B878633A5E246BC213CC98A29DC453CD6A70EB7C584DA8A9EB3085913EFD3133FD501ACCDB6A56FF9CB6492CF486BF4413893F8C5082CCB9B34AD8ABE77D258F4915D38F9315DAEF10D4A067A8FDA7250098796894D2C2EBF5A3F86B6AACB8CC32D2C4A054D9F7B3688B84CEEFA84B2824F01DA0AC4D69670ED08804FBCB9AFAC506AA3A25865B0E7C8AC5C32F3031C3C81B5675D6914CFFAF95368F08145A26C8782FD89143A6626A8EDDC3353AB822E0F1CFA625BF9B83D768A6218740D2ADE5A4A6B45F766C04A9D9B04AC80E4D2CF593ACA7EC4F5F33E7C1912AD1DAA219E154D1BC99FDB44E301F97681A12A52FADE47F660EED87B416B0A8E1FC4C103F3EB3E24E3A19F7F8EF79B3B2EC19187D319BF62176451644D4EC50DB448B2D22E2B3DF4D950E33B115DD03CD41761F663266541DCDD34B0093367CFC0E30929201E8335FB523CAEC552E3C7B0104BB1F817C84514D1CE2BCD1466884723B64FA899973185354F4D2827F6E7279FA918DAA033FDEE67307E68001D0925CCFE8DE66840CCF442CAE27222D2EB8EF05A07037410159020F3A76AA00859166EAD913B48351CA8938F899508413B14A482AF0B133EEA7C5EA3F13030939B8B4C313380649530E4F0552D3B54D304939624FAA40CC692DB14197A134CC7511CE0E19866C664BDF9B29AA45D57B099642A8B4CF63D3CA52CB3744DEDC0622FE721DA778762EEF66D60260B9BACE0C2B619A91DA594C5783C8F9E6B6637A77F5A416A67406311B300447239B867A5A3A205DB6BE4D53AF5B443A89BC4FA51FA6A57B9CC9FC982A00941E9165FD2CE99AB80AF39B7809AA730E5BB203F265F2637C2A7454D518762BB8F597E6771A7F427A448627D5A02DEF1B47DDB7551FB0DD57DB674F4311A63F2B2D58F8835CB3F19830956146465189D2439811D5C0531FDD8C1F5DF3114F3560EBD82C76C5FA19B530CB0DC3932B2242A28FA7834B59F01D12035B035E9CC37C31CE085C23CECA372623940DD45478F95EAA1B151B84CF2A65F550C14FD33FF67148582970525EC768DBC2D2EDBD5DAF0A44F85D5FEE6ADCB93DE926F90B9C2121D6B2A5522786124EE38F8569987E3B8E6AE3637848DE7E8F1E6B8ABB85612F6541CBCC964EB7908C6DCD891DE2065E68C4A1543AB3A51C7CEC13D3DF923555FDEFCCDD89CF2CDCEC6C5B3CF045BC22A21E55EC2BC7262269F74471C5C1B4C9B86B7FFC420ACEBF2F531492A02B76F07F4F4981DE76664881955D255FEBBD348297F3414C2B026E136557B91784C3E400996DFAFD785467890FE393C63263E98B73BDF3F0DE581E5B0E5A9554F63B5A6396900C87F8E53B7B94474882CD2926B090EB54DD94FBCEBF3399B0C495D5A5A86B879A09B2730A43DF91862F534621B49F9BF8B37B3AAAE57C1F6A12DBABB10A7CE68DEF9E04B12A3F724EEE998A7FAAB98C4F29C0F580297A2EFAF8E51E2A5DC845EA45F2D091C4D8125EFFAEFE4E282D5771BAA9577A3E309D8A6E29C974B147FD4EC003CD3F7FD128AC017BF45515655D3C6D7909F52AB191847261AB5565EF6847742A08C982AE3075EA92A1D3AE01B0B624B562D44BBC7339129082872B1CAE62C712084F59EFC13CEC0A1CDB890985EEA0AD2756CB04B5A5B85D2620A2144605C03EDE8D6164A9DC6742EC4995368CDE68C1405996B16827EE4F6AA93FE311AD4C79D4260D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+            Assert.True(Arrays.AreEqual(expected, attachedSig));
+
+            signer.Init(false, pubParams);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+        }
+        
+        [Test]
+        public void TestBasicSignature()
+        {
+            SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source (Hex.Decode("7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4711E95F8A383854BA16A5DD3E25FF71D3"
+                + "061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"))};
+            
+            FixedSecureRandom random = new FixedSecureRandom(source);
+
+            kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, SPHINCSPlusParameters.sha256_128f));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+            SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+            byte[] msg = Hex.Decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+            SPHINCSPlusSigner signer = new SPHINCSPlusSigner();
+
+            FixedSecureRandom.Source[] source1 =
+                {new FixedSecureRandom.Source(Hex.Decode("33b3c07507e4201748494d832b6ee2a6"))};
+            FixedSecureRandom fr = new FixedSecureRandom(source1);
+            signer.Init(true, new ParametersWithRandom(privParams, fr));
+
+            byte[] sig = signer.GenerateSignature(msg);
+            byte[] attachedSig = Arrays.Concatenate(sig, msg);
+
+            byte[] expected = Hex.Decode("B77B5397031E67EB585DBA86B10B710B3A36F9C5CC3A2224371A11E36D833073B666C5A7692DAA400171A5BCCB0EC99CF2F67B0CECCE24F4538A20E1671E8DAEF86CD5F153AF0EFD5363FE52C9855777214702E497AD64163C347181607B1B3710175AE1A4B168EA194E8D844D55D3D0CEE10CCB310BF4BDED9103202907D1AD5071672F3EADDBB70EB09FD2FD2A0A9793ADF79DE0731D56B9C5270E9F02777ECCC9129B34F2FA7B78AD28DA674C95BE47D4547ACC18F5E3CD0EDF42C51513DDF11D55A1E97D944800BE90E550AF9555022F0CF64B56F3E9A769ECBAD8BDBC18C4A686B76CCD3EFE19D122C89E65F22E70F1364ED0CA95E7DC9A598F6CF1C5C2D1A9DF83E3A339B845FF7B0B24C695378B995312A3BCC8221957E9FD19DEB2681E03556C50055D5F242A7640791FCBEC36EE50860FEFFCD2EADDB7E01AEFE0A13293AB583D13BB441BBA77248FA235A652BA690432395DDEA999E2136E61328F732FB0EC93029214937834A75AF922DA4047D552AFB53386F6509603E81FF1AFF7DD6DD39C770DEC1FCF7E792157800B13057162EEE5EDEE94A8D92432019B8B918154B1F062ADBCC4EE799A7AE87C327A22A4AEE41E1DF8AC0B8E037415AD70090E974B2C7D1890B0FE8B3C57E311D41A8B8B3ADA94B03FA9CA53561CAD470AACF97DF0C49B14F903E4DC6D2B1B9188D361739A0055AAF14CAFB671E17B28FEF6ACDC760D5BEEF07D45A999DD40BD55F37DCCCCA84A9972E88442BA10BD726A0C3FCCAB7235DA8D9D7821F2C4AF1F76EA52C8D7EE5FE4F81B454283A770BB9CA7E49EE08FF9BA86D6A82DBC637A1F16D1AD07132ED8858644C33FC7FDE8CCBD751B221B7960536A6B10924227622A25BF7DFB3793638E5F09EA819138C9D8451C4C00395F30B83C2CB9D314DAE6D468D4BBA5D70F29CB95A5843D7AD3954D0427861B063D2CAE3D8E1C4D38555E48DB82C81FC08FD94D8E9D473CD8DB8EF96E3B88F8AC30A124FAB8C9738194C560EC2D973AC55E06A95EC92F66C3F00D420355C5E8883DA9089D58D0E49D1E3D9B96A39AA444C73E954DC88FE53DBAFE22EC91300A0728321766112C04B55477AB9C52F4682DE3E8F53A32B452CD0F87DAC836352C2677BA97CA1C591DABEADBEA61CE191CC30B3CA0F0976F2E93287DB7C0CF1506826D48BC5579DACB6FAA404F57FCCD6F26728642D77DD0874DC7CCB9E9F45A7D36C0C2C1C483ADAB69CF445E56FC5D44ABA2AEE4F6BE772D330FBEB9FFD853E3C0A06F165271DE04A743D924E4DF03B48EAD67FF458968D876B7486A25DA048909C892E858995F433E791D86ECCAAD1B2C1F6AAC92D994A26D24E960A90B5537C3C679DC46FD714E191A50E2798F794659AF8CE73059565899E26088FB0F98567528E81142A46B36853100D378FF00AA389A435AF92A3472BD20F0A00FE1A553E4744B6D4515A945FE1811C9F7B9637BEACFDBABBE36CACF31803EE41A8020852DBB1BD67EF8AA003F06D6EB371B1DD4024E4347F9381FACF4F7F0CADE7C6A6586A4AF1F9A240FDB3AE3CDC54906F7677D10E3A83B31B6F48B993A5AA07C6C68D2A30A3F8DFE3434C548745ADDA40C01AE58CECB64866A52C422A214CF65D243EE3F716240D1C76C066DE94F44387A14D26C1E6190EB259856D4326535541C7D526A0E4A5A80DF931C118532518C321B7F1FD22784D0738F9C986752700019E41B789729F30C4013FBC69199D61139D9BD8CB686691ED11ECD5025D1E6123FF9240D2A2475FE4B1957214AE7EE930010AA25CB19D619734F8BA013D5895AADA9C5DCC9E0283EBDE787C3C004C0AD4C120C0D5C68F5BB9C346436EFC7D40B772231CDAAD2BA813F705B778F2B77787B85876120A36CF837A70045FDF47AB84A136787214EC0D5EBDF685D6154CBA24B5945AF73C7410B80DFA607A478923F5DCF4008FEF72C17BF0CA115C559A43774A9E2B72D629DE3D7610EACBAC220327A0F095A883244AAF1F8D901B935912C2D03D27A00BACD9F2E66C6FB6E1ABEDE95CE9C23D13E38EE6FF8FF32DE2EB370CED90F863916C7652FA24C0E558004B128468EC24D4871C072D2058F51729EEB73F0D2A9FD7C835853B7027913ACEEDE6A0F1E4552DB98724438ADC72C5706162856F5479063004A1F0BC9F92442B6273D2562E8584850BBE47143F1E1161179470C59A2D520537FE289C87FC30651757A71AD650043E5617F7E256AAFFB8B6539F59CE30B1E1DD585DECDA0E1C0B16A27768BC0529B593F76AEB89DF6F6E7411A8AA2FDB11F9040FB5AA21396C2B993D1F7F5A11185A6066878B2781B477AEB43C040B4837D1AAE8AB0CFFB5CDB7D08CE197FD01CE766B9CD0C0BA99926233112C54F50605F64CE35FB4553857914164F67F44A27EC6507F0A572721BFC95935E1D374A9E3738FA83F3AC8F07357917ADE08812D1A8E75031644CF67F34686A8E2643D04C23250B96E1AF93630488AC996C25707C1877E1C49A60B6924EE88D7FDFC7C8A98AD91623EEC661D834DB8089C411C254F2D5A8B77CAB4857C578F62F17F38A1F30D7C6B3E8F69422AD4D86D08DD55AFD8FDCCD9C4942595A1DB0F86A8689AC33A1DF9A508417BC74106686CDAE0E405799707D438BA39F135BBAFA35D252B9CBFDEF0CEFD98B4A0AFDFD460EA7B8C384BC1CE5AEE8C1C1A16449237994779CB88F1416E910A4B37FC0E719BFC555E494CBF05661EA206EFC53B6D97C15C99A99EC42F045295C4303CB0CFD8A5944CEA384D5AFA8ACECECE264DC13BB2542CC74A52D7608FB53C9E4D4F27ABE820C01B9FC42939B2B4481CA5FBF9EFA5FD2662FCD53ED21063F2CCD4154828D8C0A8E9AF2710521527B866FEB6ADBDAF9B2C53323999552AB300DA1F605FCF5EC4B92BE5A8442D89268E022D99648E9176F975240E98C97DA91E04AD59883560261051C2CA9DFF8FCE8D65158841BF7F07A5CC93A405A8F14720F2B250FF6FF64C08BEC145202402DD611E99722BC5EF8652F748BA0595C83F788E73FE17472C284587B8AEF53581454C5E850F8D389B56C4F7BF7AD6EAA9FA0288DA9BD7A3B10416806D02AF6901EE29EB910E7024C65D10E1494C349C8C9E4E7E20A9B80479612EF652E8365541482F6A47AEFB4C89F6464AE12439158761E2CC0572E20CB0A5BF24715A237B0C3448A9EA51DED0E805FABBE7F6997FC12C8A65A5B575F499624BA52A25CA79C032605CEF3E485F587DD4B283D20727969459AFD8B89245A012F6202A7C2490D9A2B11FB749F855FA20481E9077E1DB86FFCA49720C1F2C6C65B9744CB9CED93621AB30E37ADE5BC1AB6B4E67E84020604A3116D440345126F487A211778592592B18C295E57D3FC1DECAE620338221A607BD1E3B89632B0BEB929256A4A4F178CAB98C7F280BABF826784D50CD724FF6FE7B173D452579DFC460536D35E2F765BE6BA17EE9B84F25EA2CC96C0F6332ECF593893261D3A062B071B1075CC2132F04D45A8A445729081120E3F36DCA6C7DBE655A907687D985D3F3477E84D8A81EF63D2D05607CFC70AB7A3DDBB1199EE5EEC811E23C29B8A68FC47BE95F8E9FCB36AAC17263A08B4B2A33436C39D6EC5136A4C3CFBA19ABA3D1C4C418230037D358BEAC6A826EAEAF095699D5E086F9BD0FDD70B34E8F07A3292A296788F5A909BE23E3748CCBDACFCF056B14B6A79108C17DB0A704B544713CA9BE09F4FF3732F4B30C8C91E23CABCED533917E53A3933D7C70EBF519A5EF6E54212D7A8A9F018AAFB8AA40FD335243C96D484A9B2A156B04F779A5BC6986148AAA31F925533B9078F0EF2B2E5224035639CF2E49CA836CDCE8B03413D10F5E730CA546331ED2DCA21674FE9242F0CD0CA38D8537E07BB9DCCF44F0628BBA9B47B89849AD7063DC7FA3E98689FCC4B656BCDD46F15B061B5E05E987B2DC9A2041CD008B1F17D53ED38B4447F5B9DD9A9C5A588A5D603C625FC29D3BBEADA79A9F6A41335F3DABEB07D73F4331CB05161D241B84A92382F09594B5BA616C13CDC597C9DF0AABDB6C6F5571213E8EECDF0E4273CA170F07F3D255CEB00C837DA16EBBB03919DBEA3BB55476AC1B9F1BEC97A1A4E51E6CD7FB5B29598258089B37AD37D4FFF3F618114F355A2CE77F07E439FE3F2E14C2E6E705B2F5F148A3C7805545E4D7C4E50E08A4200DD6E0D9E21347E01E8F45D7B1787777E1373A6EC22A50C1C010F394EBCFBA36D28B1D822307CC7DCFA13AF8C61C63C4744137827BFB8F9F0A01CCFD7C539CCFC9EE10E6AC0441ACC14FF443D0E2BF361498118451AFCA362496E7B62737045E434012AD91E426FE97A98D0269703486CFD5DEAAFD00C659CE6870C8852C3C6BBA83B6B9F3FBCFA0E02BCC18754624E6598A8D1DE90CA931607D03D4602D0AAED25942A27A28EE1E281264937F4A7C785B7B744DA2601798B48B1618FB2486662585849B4F743346B672B973DAF29961E04399CB339A4A71FD044627DDC68FCAAAAA978F5A8B67625D7761E37B61FFBB6CC327113BE98D383D8FC71CD6FA9A22BB371019D585083B7ACAFA40E96AA88008A9853F137270B9B87B0D6D1E67E9B1FA3ED5A92EF8CE3442AFF5F1BA087CA8421137A69CED2C37E10FB242BF928994391E5A39F40E8095F0A2E85A587093A939F364D0AD8EC1D62C54E2A33DC94FBAE1F09304E159A8E018B74A4000CBA3899FF7DA14098C092DC83546B6A19FB67A6030B43E1F638AA0FCBCB8B3288993167C90DDE4E34FBE0685142DEEBCAFBD8FF5968D356D0C6DFFEA0C0B7D0DDC05B3FD135EA42F66279171F2D4239FFC5BE7403F498A319048F55811C2B63A3A6B631CF96F59BCC689D2890AFAC48C723DE8A66FCD2A65F9AA2AA165EA53511143FF54152E7AB64F96F5E28EC9FD8E73E3B5C7FE7CBBCFCE5BFFCCBD5C53A9F73EFF2F1C95A2848EBBCBBFF76BC0477470FE5C4B106BF97CB88233C795BAB518EBBA0EF31C07DF59EB7DF1C60676E12A667FE85E7B10699588F268679995E3A12DD99C5FF393AC01DF76DEB442D3705A4B0BFBFCA55AEA8D12C7A90D7AAE453FBBB9C23F89F8CE777D4B24DCC21158346EA2BE553CCE385EE53A1488C270175D36117D8F995482BF840200D94F0F1F86523B8F5DB18ABFC4D847E585A75C2AB0FE602B55560A6811FE0C3AC18ED5E64B585B727EAF5A75957872BFDC08166E957944604B0572355313EA82F553A69FD5DE76F3C344E186A164BC950FDE22F5744017DC6EA82FAB13CAAB2BBF71BC38AFC68FED0EFC3B6A683EE45B582E01E6EDF2A5D85497BCBF23E67BF28879B4844C773BFD78C2A8B9C7DBFF3A53C1DA2026C3C5A8A3DB0CF17549F31B307F8EACC7B89CD31D99C0BE707F4F81146F41F46DBC4D3821CD80D002945C752D2C4E6CFD18DC597768F87E572E79A9444BCD241424BD5BEF882683884E3D650DC60EB80137CEE87B2C5C958A1E29AB40FB3B98481FDB8A6A459BEAB371D01C450277852101985A54736311964930BD566832AFD8C8797DEC770B150F71DF54E4B0B4FBCF4453A32270F129A21B347514C4174FBA95F599F730A0F563ECB285484B289677CF0D61AA8B6FCA5EE6B53FEFC6AB97D763E0340158E904A74044E43F429B155772CEE802FD4DE92A1D3F9B4DA34E285D964FC7FBCA79E97C11A715E1CCFF07692027FE2514C5250FB5F9F0569EF563FEBE491C7EB22B586B13244094532EB7A5B06C59714036B457B60868C43F8F3782C01ECC8CCF6BE78D057CC8056907AD2BF83CC8BE7163B1CF43D8AF560102F2D2ECD4295056961316BE161A1A38ADCEA881166AA0E21992D2FD59449D44AC139B3C6CE0D94F1CCF780DB9054ED47D0BC8EB85F53056FA037D73FA895FD8FDE5662ED5BC2D2C428822433721D0BD8BB8CAF50766309DAA92BA7ED1E12DD1FCF02924A9F715C0802990F73628B14D82E5E9929AE2BEC5EA52CA4B79A864CE0F26FFEDFB207FCAAD3DDDDECF8515DEC6A3E96F0C292C880A5D629BE55EC417E34C94536D9FFE75D3C7CBC972CE633941BE63089F2B2EA45E254083E0C170327C7C45DB6764DA773BDF07CDD21B4D1497C0AA4983FDAA4FF2CD755B54C4046DB35FBCC7CA3C2C0BA79651FE2B9D22E6FD7D00302FBDB0F573A5722EB631EC0020A949DB060973BFD0B9B61BEDA1B0B8AB9FCA3DD42CFEC78AF467D0B31B7A78C4F18526BE58CEFDAAD4FBF204AABC1BED430DDDC82785F0BB04EDD4AB0B12190BD2D65899010C8C8E04D4608FB87D5E93122E6B460CE899E35AE09297EE514D10C375559C010363108260538273FB5D980B1C4157CA17223D506E043AA0884E3C1D1CCF80C123C900FA7900E8791EAB8071E5A92516C2941B70371D551A1FF31C9AB9FBD7B20BDEAC222B7D33851B0EACD75C0EBFC5683317E38F136844E5389B685F3CEA2846D02BEE40D5B5BCA365DCEEC43E0B4AD4CD04ADA0A45E75021F3443D2EA11FA8EE882B15F30FA6BDD55F6E44BF16FDE0963EA1A8090751F310759F61FA2028A73DD6149B716C9F11BBD9FD3DF897E3BC7F68EB7E07AAA901355C04D026B6FAC380C9FCF1B273A746208821DB3419BE8C0C38F817254D3EA273960B017FB22CA84578310F0978CF1A8310A5E35569477C9C7AFC1637B8768345DBD56DB2231A5C9EED4DE74F9C0D7C69DC8424EA2D17380F6F23CCA52DA4280AEDBFAC0B9A14CF9767401131E96C5CB78374FB5B0002BB1B50D734C4446FD435B0B941F64138E64FB4C1B488DF098A7AD20E7F5140F99D357E6DEBA57A5A3E1BEECE7CAA9EAAFDEAE26B9DD3A190DCF807D2E9063DE3026F85F5375B0945AD319F47B072348EEED817C22BF126AB28EAB498695428F10497433FCD6B4E7DAE9B6A5AAF4ABF90CD347F0999F1110453A71B55BF0A0A4C8C01413394D4A8AC140099335FFE6269775D0" +
+                "D42E0451C7D2B7F7608CC26A7B10B37608F4C578DA7B54C6ACDFD15A10783B46CB7908E53AE036D4E19A2591359E7AA11B410BCD1512C261FDF94967BE0F447BF0E0F1FDE616ECC70B17282064A0F5A89A93D3EC3ED0C49B66C1B3B2A31800DAD0CD6132D0390729E2003F30571A4BE1FD9FB64C007063004E2BE3677008287D48ADDCD949F920FBF082D8FD4D9A2D45505AE35831B2057C54560F40412F053716270F3AD2385AF6DD243BBDC4B025FCA76932643842BEC9DED4BDC5F30733B2DE487F997F5ADDB7ACCF37E5FEDB9528670401A6698751AFE0E13F1C355E729397A3BABACEFBC174B8AEF4BF80C1712A121462073BB4DA4BBB7C1264CFF119FDC2AAE56B864BA0296ACC39A59A14C82424C55F67DCA9EDB5608B3D627C136EC4E90AE8E811C919E5ED973F8DD8F9A4F6F3DD409282200F35DF3F7425A673F2574321A3161CAA86A9D813AFEF3E4065A50B6CF798732E2D7302578F7F7D56EE05DCAC2ECFF6F79F34039495E0DAC9473773CC9489A06C59732716FB304A4899D743987AB1D175031AC5505EEE949099455903505145B7B7B909C65B9533E64552DEA2A252366702A65DB0783E82C4C18F2CC604F03C6C5050FE365D4645A16356141B5CAA762A5CD20D09EF5E962E3BD6E5DA9F7742F55E32DECF2FE310E17D1F0448C9599C3889ABB9A904AB350DE7137969A2EDA21F2CA54578E077E5E2168DBE56BB290B7500654AAF71E32AA2DA571122401713A8E26F23889B4E437B87523AE9FA2F0F977FC8F1A983260ED74333E1482EB41B0C28DD21DBBD1DE2AAC0F0BF3ABA192A93CBAF6B543FF5AADB529C92DDDE0BCDE66D73B30CBD509C17D5C80C00FBF5FC8004A5A03448619CC2FBB6003D340F262B46A0B70BB6BE687FE676B7B5EF7D0FE51F349A9EF21EA95AADF3A33CAD9802EF46087885EF63FFE269366C536887FE8E8232EB95640716D7B81F71C886205240231D3201886E888C06474D14D0F26E65102A308671D61008B3FEE3E73B96E123E1993CF7012C5A42D17703DF535381042F60C902D92E55FE239CD0030978B8FD65243DF8DF718E9B308E50876D42D2C202C51C238578D803A479154315D18EBDEC4C57D3863D2478AEEE69FB416E8B3A03B44876AB5887C7445095BF5A204A29FB88D1978DAE76F98853CE2AE767584FA0E9A392599E640DBE55A63EE26E21B868F113BC89F3523F27E109B8B6BEAB6D35985A51ADF79A497CC7D5BE5D208CDD0FC75A5722265E77A608C3B91B6A96855FCA82A0C2CEEB28CE9E277F958D4F2AFF664056EB2E58616454D7E02F17DC8B17001F6010027E83F3287B4D49DE0BBB319D9E865E8284D7FD6C68CEE1E7B075EC79BBEB22A65FD5942EE568C505BA5A5E2EDD8E82C28C94643DBDD3FD5F8E13AAE3546A199BABE16F3F6A8E3E356892418594FB61A5ACB5A1C5013DAF610B683150CC4A0134604910F680121EABBD0B6F397BA425797DE3298B82496F7F4062DC5DAFCE7BDEC4991C921AD6E6C5758BEB188C76B88DB3A012091584016B5D161AFE086E4849F201742CD0FFB02803148E2CB262CEC9AFF6448370FCE12DF750DED05805C788FC2AF93C39CA023FBCA8D99BEBA76721BF166183ACAF2E3EC00B7B716EBCBEB73F78E93492FB0415FA3A1CF590FE73741019AD648B9809D94EE647BBEE3382B608A7D33EA73B80D51C44A064E1A6FEC3044C19001A069C403B13F7723D9EA0948AC8372AD82899EA895E6D606EF170AD860C7CD6F9D769A6C78A24450AD5D08A9E2E7D343EA9DD3F5F7B035E8CE3D7D739D30A253B80B17711C71BE014F627DC96E1A223584A687C74ABD8CCFA8925B84D8652793B65EA33316D7CBA930960F4D50C4857B7F367FAEDBFB64E4BDA092008658EEA0241A3387B246E2E042C1AE9A5DBF30CE5DA3A42D775F9B7868B03A5D1A3980386EAB9606652593FFCB7DDEFF03729CB25A736901C026DC2833D57641899D69642E227126EFF37EFB87D6679869D574FB19BB42C64A4549CA1D47BF495489D10A7DA3DF99E0DFD68E177EF8FD92027741FAFE1730AF167C4BA99C8EF0BFC6F96EAEFD756C8E4954B9927787C0C53B1A26EA0851949B928ACC9341241DBA0A6BBDB2F53BA6D57A076CD72F3ABF0128183CFDCE835E2102AE27DE63F717E6EA8F21F90E4D27EAF9F6F92F7B4D1E2C0121731BE18E3EAE2D4A667BDAC3E8956307765E387745DF9A2E40C31110661CE474ABA673E0B18BFD5BD680216D19AA037E5E2D5FC6FB2E311E86DE3505757E081B1B4CA167F4F6AF1A658F3BFF0E8D3A6A4C8E01792091A24E52AF0E5B864677DB3C1EC1600A13EC9ECBF886386B57B4BC2A1EAFC5F50BB2923440338960AC38FCF39306E7C0724E372E8E7629410C839D238ABC18A6DF8D9A79BCA4CF5679BDC2BCB685924BE9CFB8191F2472191990C714995CAAFAAA261A85B272FBFB81E43BA7F5DEBFE489E47E8F19AC572F7F39998A5EA735DA2D30A471CE9DC83174948453E9CAA9B86B00453493669903D81905D3FED65B9447AAA7F38B8949942E0D480582AC3713383460086CCF167BB8995B7A4E033A53C4E3723A4DE73446829927E2CB2A3A1C14F4CFB1AC3F906E11DBFB78140B27899099E534594222C79C3EB9893E40C7274CC50F521E7F24462E346DF963E37303E9DE64C6777A8B91B8516FC7FBA1BAD060ED5CD5C7E672D033B461A882E289F2313A58CF3A88C9082BBD596646D97440D85DE1D798EDA2E74FBABE1967926A07C4444F827EDA8F1238BAA15ADF1DC38307F7AB2E3DC1EF213464CAE3B8B1C4BD37FD50957CDB4E317869813F88EB9378F6149E95D898D661CA3C238C86C180AB40FFDBDD5953A3210660F73D69AB9FA3EA16D25171A38DE4E0AF1FC77BB0C6968D3302DF7261744CB7C3583D581A0D940EE12077988D99050AF06A52EC210C03EC8067CCB046CE641BC48EE407D67618A21F31E767AB24A7DCBFC94DE22E866B4EC7DCBAEDB16107204EC39882D9E823B16DF7999291AA85507FE8F89BEB2AD7DBF68FD728E1A2D791D0E84DA90E52EDCF184EC97F4A1C9FFBBC8611357B274169AC638CE1130D818313950908CB462B261052B4E41D11E1A934057C356EB74F8EACC8B39847511BF4918599A338B134F5040A42588A742271F4DA751A52920C36CFDD89719CB954B7F225D895235034A9DB11EFE7AE74DF329F2CEF61E63D6E20853CC9E604D1572D996728D43B3A3B517FF1E8C2229EAA31329E9860C246BA8D3252A20BAD15271DC8B2ED46BEA7B6B33385C24C590DFA69103E60D4E62D844485ABBD874018F063758EFF0B47AE740AB5524FEDBBFF664F9FB1F54A3339FEB8E86266BEC6388F025F6D7FC7CFC4DD92C069DD4614FC2AFA22C63B9CDA8A01C145D35DE9523F245D4DFFFE37F006501E74D0E492E92A736B272DA4387E0FDCC51EEF861D87C8E7097EACDDBE937562E8FBE8EF78E22F1D78F318C8EAE7D4459EC074FCE5BC56A878FCF7D99E7D973F58BB6A028F53550141B4B1F1AB828FE924EC1606867D4ACACC04226360CFD57A109881D66B6976C4DF6DB21635A372A41ABF49C8AA8694DA3F624B440B88B658FD75DBA26C02B5CA60677C60D28F24AE95BE6A0FA8149EB90AB4CB91C524F6EDF3B6E3ABF20AA04FD52AB47B6EB9C090FA7E6696822451304518073D7E4C173DD3E2D8889F0B21129319D8EC5EDB2523DD6BBC8BD0B1622A3B52CDE42728F4715AE54E62665B7453C144B78935F64EF57D2D0896C07BCA7B8447F4109DFBDA4734E8A2C0CB3A87EECAC77F49FCD30AB7E4B4DE524E53ACA0BED1554E7D13AAB94C14E5B732B0351ADC55B8F82EDD296AB73F25832EF44080C13CCEF5C5F8BA45CB3A5098DB5F9F1B89199AF4B17E3F70A3680FF8B1513C8726E42BED5EC5F79C4F754D98767994D7802B5FE6A91743F2821E1FE79A9441D6CF9A7F285DA22C87690B0BB8C0880EC75382994F5A7F909BF3BAE5233EA9DA1364A4B7C271D4F63AACC504E65BCEBF609FD66DF6A2B598C667CC357A3F6C537DCE76C22B5BA2171EDE56E686EAD003AABA5D7E9C5F595A81379E06B7AFCF0E8B5068B729DD6C697400D81B7579EC3C7314180CEFA6A55648D2B0E4723520CB8E7E7B1F86FA59A751CE27D2AB07AE2753558B52C12247DB6FCAA2CB5E0E6B8F2565C4685F7F9C317081F2007EDF166CA13F99B39B8902ABB6144AEFEDB229A79E1E0658B31C19FEED3B92110FF9666654E4BC4BD653AA3A8B5B8115FEAC5C5637BC7F2A6F27B03EFB53F493AD1B8850C7854FF856515530EA542546A83DAFB2577CAA13BC433EA94931A3A736925C21534D06BA2A089052E8351FBE64510D16CC50130169CAF78E23B64E8242D521D892D4CAE0F22D7EF20B9D4A3B49804C02F2873E23CFD762EC30EBEC27A856F72CEDD9B4DD1605FB7BD59CBB464D4EB4C1CA6D2B5F320C881D00F7F84E6F1D151C67DED339F08DFE751C4D5CB40CD9E8A22A60C9F54F38227A3219C7E10768C6CA931EC8D19999C8FEB52B24674652E398686B64FF71B704194A1FCD66CCA0667E32B41F8FB70F81B18E2080CD6727F92C22ED5039FCEBDB6802DDE8B6529D7AD2EAA94778118428C730C13DCEB154B08E3A58199BC258B460C96A67895F7486FACA5E78078F5E33D89ABDBE0704B74CFADB899FD70924B0203A1B5E16C301C3D3F6CFF1230B6E19757D05B2E80E7671FEF111E71037D5AB289CCA81E0068F57DD8419685F3F752E72D3AF2588D045DBE757188D3D5B8A38640D173FEC10EA01DFB054283E82D2413DF332DABD7E95258685BE57102F03DF01E8F1695F8FD25B134D3D103841357E250B5333075DB34FE5A0C17100588397F7D93AF796D4655A4D6A1AB3B1200ABD5B0AA1A49476CDB8B4B0FE421EC4C39DA555EE74419AB0E8D4589C14E11399DA66AE778859A1C34ADD195E60E271C2D38D6B705330877B84F7C986896356C1B2FE65DEA14E2B9A1FA5E24F649F915C0BB12B5807E8C6FB32E7A4C9783AE702629401C48B53C65DB7E19925FAD7E014E5F2F175AFDFECA3FF66A40C5FB8A07F4711ECF27085A577FF99E3407211E45DAD212ABD7AFD1CAC3D08CE5D5D3735291C75247C018E60A69218BBC3243FEFC3E0ACEF51033E96F2F8A775ADDD75FFDC5CE309C7B68998FA855B4D0267C877798A7CE0DC3BF2A18CCAF9CF167FBE25F665C35459FBC274301D7F258F5435B025C3F3220BB9F1D718854C586C238B6EBBEFB7D92634CECB2375F836F7F567B71D09F595C749AF2F984E08C9DBEA31511492537F74DE9C9B88C3BD33FB273507B956461D3FA630DBF51A6854708728E64D10B3C6CF1746A50A4A0D84FD7A9428AD147A171050FB276C6B6E3A7CAF4781499B31715EFB3B0E5DB33D8A34FB2DA1A77239E99002470A52EBA708DE93A1EA26743F3D8225317F4F88798B906B4458362FF819D487BFC8770418C959FC147C5A729B4EA58BC3CBCD66FEC3BED48389184908005204657DD0C7999B68EE95F60C1CAC93A664DD6A14E79C8FBE13DDC13EEDF6438BD76BD3DA1FF0BE38DD9325A2F7DD6C4585BD4C487DD9357AC15A8732F14AAE84902CC1B8DFB98B26D0C32DFC3238E97D9EFA18667EA5BDEA6E7BA8BADBC76C0F22BBC0BA2FC368EF80AA1DE9EB0263B6635BE0F9435415B7B6B4FE028EA2C57B79897FCC7F208E102C9D4763A0AA10CF03A76B9045FC0AB78FA2AAF7187F236EE21F68D6432A96CF532CA72AC1FCAC9D38864ED36D98AAA00F5035367EF260B984D9BB9FDA9BD4AE02DFC3CEC33BF989F0752C997579A1C6585C876D28125B1CFC4DFB03BBCE397B5E8C49BF5A83D73E04A27001C49F82B820085D44FF353371AC7E1064032D271887CCCA77F4C24FBD836377E6475579CCA23855E0EA39C7F762B00368A3B36AA80AA0D529AEFE0C8FC098AF5911E845E78DE8C11081F2DABD40BE30AAAB48B54D3767B498F6ED95CBE61B79CB1FCC0F8C90923D37C8B7327CF1C5ADF00CB32187357607A2B38F5B66F62D293D91945C9054752B1C82A8793CC1B95F0170F7B8314B4DC954FF68BF17F1C2CBD3859CE3FB64DCB6840E1EA129F07600FBAD0ACF27CEA02103B7FE8E7F1F36FF285686FBBFAC4C774F9B9748A293BCA234221C4BC9E0CD3D97DB56F542D18AE1C9A47951CDBC8722178FA7244DCC7DC56A7810A8F10F9F1C291D95EF29363762524DDEBEE0BD7691F188E1BC412B9289911A91D12C4E6940D3767DB04493C66B7453D5C2D22B79FE36FBBE273FF3A88A8B9F05C30B45BEAC15153F093C4A6B932D1FB00433BF7455F654E7F75AE03E8E5858B0ACF30071C677A0466A15C5BC94A7C46251AD0CAD7E3DC36F6D184379FA8DD357D2A32EA7049BB966215B0640FC01355204D856DAFD52E0B40D82D56B16E9A15663262BDCAB1A0DB97A589A52FEC0C0C4093356601796827A0064FCF85DE67DE7BF5977537EFB0D3DA0D8703269A46D0D3CE65360B8D995CB6C00D126C824C7D56D741C3DDC724EE9E2E250116362E9079B7BF4B0E9CAD71EA8DCCFAE3870E31189F5B80D16E50BEEA4511F4EEAB9DA9DF046EBD001F4038FD7707DD9C31825434AF11075CFED8C2F7EECAC2FB34A48C94D912CE26379FD320E3A0FC38A51E2D6712FA9FA993CB6B34EDB54CE83FB9CCE03E5E55BA2909F4BDDF34F17F33C5768738D91E098BCE919F9C3F74580A6DBB10CAEBA9BCAF6A8CD1FEE79055EF30B786E0D8CA75B7FC876FABB5408BDD95608D12C10FAF035FEF78B4B7E01A3BE71F006CC73F00E399BF1FD42317B77EA2E8754267A8B4DAB9B37E06D9944003FAC1F35EC83936750F0FEBC34E94927B003BDF41FCA881C7618F1A0B9AC900EC0A5C34EA0633B1F8149CF194489C8BD4E1E4F40E2E380C66DD539A3DCB80BF35A0E25D30B175A41E967C7D4FD676DB30D434FAEA802B9B72DB66E701CD3E33B144F87724BFEDD2B0BD92AFC0DE491B973B744B88BB5BDD83537E27483AF04BD5790E29ECEE97876E47A1D200785CE7532D767E1D04C44BC266D201F34CF0F5155A42573892F5CE26E172E4CD4E931A5CEACD2046FF58DD7AC8ED0FAC4737267A69AC52D6BFC870C00DA19509579716DD38127EC7F4B471949CF85ADA6AA75BD9CBA7739428D8AE9FFB3965A728AFD186C0F06C75A6073E6A8F39D6D78E2EBDEABDC6247030B221447BDE32AE8099FB0FC7B6B03E036BE098F25A428266C77AC28B0A16A7CF4A8B22089021F2FC1D98C53889C7A1CF4C98534943A1632A38FA86DA4FFB4252450891338946AA07B58EC5D57347FB6BDF0B5A5D61AFD223FEA965A1E205F0EEA982F36029DEB8FE8AB66DB9E464B17AA416A4D1DFF0A7772AB783F139ADCC116A23093C3BD0D95738F66D8D8BD6CFEBB544E739E162AC34CD2E21009B995E8155ED2964CEA9DEA8B2A09F5FDEDF7F957C1C597474B8EBC721DC61CBB582E0EDD6B01AD3E526177DB0DA9B45A2CD2722488231F55F0665F3166F68D0E25213C8847E9BDC44101EE42CBA6BB40F0096A941AD4C17251393FD1293CD6E66C5B7095F83FE232F446FF2C8DC33F88FA4863A7BD76B7C31F6D1E719352D307AA94B322E8DCE0DB9726059039BFDC4BE015DE7BCDB5CED46C0370B6573DAC66DE7687EA69CDF7570DA816F642108EAD77F4657C0B2AD65DE3CF9EF738C53EDE4A32B3E1EA5568AE3C9A0326AFF7624062CBF3E8511731CBFC991C3C58C517602DC7A812840DF10319A383A42BC268A2B6BEB9CF76EEE82A8B13397311101E8A36BF59A34C9B5FD2B709BD65415409472275CADA1512163B5CA2DD04FF51C53C16B2616DF3C82F87D1104B641977FD7ADB7B7831151D9CC2CE13F2818B01EF8C1CAAF8896A9D00420D913C21459C75CBB56C2010328E0E9743E5F80BA1543F55250C7F454AC805F361965F02B523F08C90D2AF978EEB105B20DB1F463623C56583440C831FA4C592A4D88442BCDD8E012A12F7450E81AE8D3D07418943432DA71F38F90926B67EC5FCE524B064CE3FE44D49A22A87A05DCD247DF95BFFA0525CA4F1160EEA69EED57AC26474BC5397F6BE6EFDC3608C18A121F00E8781C97C70C35610EB0D870E3BAA093DE905158DDDB405F3DCD994A65BEC36B22431B17B5560EC6E3AD2034AD12264E5A3E2EB4D55D520287C5CC57EF3B734BBA81D2B016CE587168430205A77473D3951AF22CBD2578F2B0BC0B7809A07D545B4AA0A06F1E593D8CF34EBED127ADE073CF3CBC000EB63345DED4E64B987FD1D6257523D2136F3843D6CA5233CE23BC6ED48673890B0E879AEB77487A5CEA8875DC9A9CB2EBE755C9E91A202B313F67CFE75BB5CAA42C97ABB31BC8A084289D8976ADE9908401469AC73F2331AA49CE503BDE68FC8F83FBFB3A12847B1079A5ED5912BAAF3970A26E78A94F7A68D14E496D7A75F1BB63864FE444F273E3305C29ED1E00ACDC7BE47A0FC398249D5E63E688FF5BC28488A77EEF13FCE338626E398F590E96D4A9D523F26204326C41876F2DAA11732848F720E954F3773C5A137412517F86F719FF2572E2F7578156EF13D6A35A8893BB333100D1C92A98179773C2520A90ECB1A90A3D610D07EE17A87C0DF5F92212E05E65D28FDA7D1FDD8F649B6A6C1A51084D49C7FAA831DC59519A301952B2BFAC6C9DB95698EE4A9B680FF00F15358956171386A8E1957B00FA51F6DB0E0162702FCD514C9497FE8E0466328C51F070001E0BDD91BC06C2445FC0BE6AA748DA1945722DB9A9034FDB5E8A00D056292453CC8AB2984CD821CB8BA7796AA2AF53EDA34210FC8DAF3165A465F40BA92C90FA96232398ECA4021287A15AB2E1D9F1AAABE1773FDB875EB234A313975A854C36E28308656CE98C087E4872647D1106FC347463F924B291D7F6DBF714FCCF141A9E974AD6CDE14E9CF86061E2CBB40247BEB48C03C2BD916764B265C3DAAACDE35920B745C56E912DD93245C40B38A41B9F7A3D725D7BC4BCFE30FA84BCB0E21F383764A66DF2BCFC1793F75A6D0EE8506CAA606057B52C4F11AE869BD5E19797CD7133A026D915B5B9B3BDC46CCAE4573960FD82A955C516EA0DCC5DB7372496E7656B69C331921F72CAC6A7741B4713B8AFBF29C40E0AA3F7117611D2CF99BF4CB0A95F8B42CA946A7A25BA12FD039BF83D3E446D4618B735DA2BDA1FDDDA89132694A14949789A22FD1E6C6168F365EAA5147205DFEA168B72ED5DC0E1A9A97392D5C1169961E3197FE086DD94FA1A0C171A88A253DF93E1F089C613C723234D496EDA57A1EA6F429BB152AC11901DCED5B1C26FE04D37D4C05DC6B0B1311DE467C9E5FBFB45DBF425EF3B9B0DAE52C56A820E922B2A07F8BAF0CD7E3B8AFA9CF98AB6B4D352A2D6C1F6D8976785A6D05D67F7F2F7F610EB390C1159DA261A092988D8A5E531362BD4269574BE3EEE529F33CEAD0C9B3A719D9D3F7E77E68E5B8899F6242A2771D16D3F0C3F0CE2236C36F4B9CD39FF6EC52013A0553A2AC4F1FC24C22A032A97F81D4DC7905C6D37B746277A22F6FEDEEF43A0E1B80EC877439CD1C1C88CC29ED6170766C70D67A4CC8E4026428088BAA4DD8A907B24DB5AF13FA900BC3D98960FAC305A40AEE6FE35D361A792E5F8DCF580263198C0015DCA966E404931B2942937C11986D6590835E2AD1994600AB73FF82CC50D8CE879839FE23A68E7C517AD72650B4168A95FA436B58EF1DB08BFDA1754A14B31E8ED00CB31AD7D5A48BF70C3F2F2D5A29E8BD06B37F97819DB29106B911949D2BC22C9CABC02B1B1D54BCBAE5368F44D0DF928A4A60692D935E1687B357546A1DA980E3D0C0669F92D99AC73D4CF14801C173387DEEE3507297195D6F988D4B7FFDA4EE067777D07CD9FBABB96A5644D64E16F6E24675C99106E8847BD2415D73FA3E32EAC68CC641BC39726D6A10A2336E95A57B72657A8DA1965A2597B5709E764F3B624CC6ED8C80581D28A7114EDBBE4E969115338CA90557CAF9EA4546192883D3EE2E3A4834C87F9B0E230571773ECCA50F79DA69AF5E7843BC14A8DC5E1CDF279569B2F962D4243900709091BF860D456065BE00C24AB26E3868BF9FEF8712BE33C5BAC3A8120CC9B65C75027C2DEF2DD2850D4EA5A7B0CF58E9228401002A86E00FAC8210B42498D2E1BB8BE1A8CF25D9D896077928691A9A431CA41BF612B5B734043D58FCA8E0B36EDB5EACB2C44E1C309D54AA9156388028B8B16EB778CEBB61A4534A0781714B6AEBDE44A4574C89D4585141CB3938AC381AEB4EC5C9C1E8F9EF408CEF0773F900FB5C3BAF410E34F577CE0E2C45CF6870355FACFD4C25ADABEEA155BDD557C95A969FB9640BC74DE40D6C905ED8E55D3CBCDC907843C4C96BF6B114FA06A18B47A4F2CC8485C6D3EB071012604D85A8E26131331BADF7AEA59E42EF916BB01A159F3FF1ADE933D346E77ACE0107FE082457CA7DD406DBB13E55A6715FB1406A5B3CAB76CE633BAFBA13CBD8E451715634704A350073BE4C55EF99C4739539D55161F8378BD68D68C8C5B9AACE7935FD1AD3444A61B03AA7217F8C328B8359A1EF1A2D51503404B74F5BD3EBAC047C54ADC95B25AA357040EC44499D7E330B434FD38331AB7168A08A13068FBC89FD0B116D6DDFAF7D40ADB617B6108E336AC5F466F7487D7807A887F03F919A3ED00814D1CF3DD51F8086FFA2B96583F88124526ACC1594AD44503B31D81E24446632F9C161BDC104B9C57A6E2BBC986E1C76545912F8723D67766FBE3C94472869ADB5D68D59174B678CD6A0E705CFDDDEB10431D2344A12666AFAD27978DE0E910BD72C78A40D907D40984E6934DB60173F584EFCAB2B64CDB92DDB10194809915AF60BDA815EA4C280EAD2D9D589953B736F4D124BACDDB1678E064A7BF346AD764EE5B315B52A79E8BBB87230E0492C956F140CE0494BA4ED81A75299D9DE1B8E50DAE686937AEC54B953D65424FDBDC0C2A5682EC0E75089F9A9254B361BED3C5C5133F3F6835D905B1BE9A17C4F3B6D322637EF40FECA00894E0187DF8EC701C28B744310726EA9E322CB8D2DD159E090F4BB05F5A49DFC60D1DFA4E4D9EC4DCCF62C61268390E3966E899E87822A687AB1887D7AA4864FF3847BBE27FD93B60D0751748F01B92532BCD3D12CB3A344AD8F4FF29A93906AB3A5EBB485AB565612E041B08DCDD7FD27F4B199122049E8B9B3077A4008EAFD9A6DDAB12472C46FA67FBCD9A4E802BA065287A63096DAB6EB2E690187E3F2F53456A812DEAAFD5D4F99EED0EA80FE32A81D3F3B62279E5141D7B86F8BE13B60F35D98ECFBF1ADC25BFFA1FBB49A2328FD2A3D042C9355C2A0242F7F3F29F8B5971A1CD999ACEEFEA8E4F2258C3CB90A184E0C4EE6ACAD96B7048048DA7FD4DD91B12FA6678A9154CD9376BC9231B9D8FE38344245E1F2808B54E2F5234463C61CC3C16AD68E953E71894F416BA6B73EC727F3612523D9545063FC5FF1813E3467207D00118A02C97086EFD1C3FF9B2DB525B38B68F517834B8EAB55795BC2458FE88C0180C3F85F323C859BD09BA94874BA3843DB15FB73788FDFE02377EE608FB0C0E0900252F3220CAD2E503D070712ACD73559C9FC36853432A9E976051DEAF21AA060F919EFBAFEE3C1F3A2F4C0E693CE635BA92FE4B985CF9F5AB2FADBDF7C14524E120647E6EB0C59A70948714EE35D235A6F3973A60CA43F60BE2F4197B3746F9BFD02849A39819A5533C850E006F897AB198CBDF60866A6C034F8A004B16C7E287874AB383E428DAA4E3595FCB5529FDCB4EFB4D78E93CFF45D16170EEF5EB264E348BC324015BF9CE316E844D66CCFA10116B4725BFAB978F9B6EDFA1843D6956F85BDD747BF4AF96302464E8E2417A5A6E172C4201B9DEE6946B06231777AA5296D1946AF8BC6021DF693FB464A99932DAEAA6B16FBDDBF283D82D9BA269B96D58E6E2531126421E30A33B2634A3BA372BD09B00A1526DCBADE3CA2B3BBEA156E539EADD30E2895D90905788812850497462CF4F9B93AF2C87018097E2E78E749D110B08EB2C3B0D2ECA914854E7F95CC8001134EC3DD9F981AD14E4093A5337609D216F7F69BDFAF16CA8A6C0FF4F5D7FFFACA554F91340E4A018AFBC5C13BC2B58C63764B2E32DD71BC2B9737C73D99B9E87DD5B285EA6013F368424A27EB0C66C2D434C33F1D45746EF0AFB31AAD1546E27B08D7D348463A3C530515AB0594A0A5B5F4FBB8C4993D5B5E21936148FDE77E9EC12CFBE9CFA4285437A4AAFE6BC2B57E9F72CF78E478EDC97D6B8197D96DF528FF0127B93AC157841F1F09454FD4A3DE3F6926953DFE69F5001EFFBAC03CF0A90E1E6D2647A04E675EC37D391B1BC6F1960880FC7BC37C94821B929CA4B78867CDDE7BB94046DAC33E38954E09BEA22480E14899C7FF50C31059CFA00D8C735E85579559751F269842C58996A3F4B3F16361E211BD25B1C82CAAF7B690ADE068D2CE81433B5CAF590263370D0593B81497A2C05EC1E87AA5DE98A353C9E510798610E2D38A81150888BAFD5383CA4A57BD40DA6BB2FF33964545AC39EC71B0E2E43EF82F0FE01B07C49691D0902782886464FB94FBEB9B0576689CFEBB929BB547DB3A42495B91ADD903ED8FBFD5C5B63C1E3734827C1EA84ACA07325139EC055EDD5FEFF8417427321094AD4E761373AA28A1027D3F1786F00F403390DFFC7C318C35FABBB216DAB17AD0A16454C4FACC68A39ABC3A2C09D0773F89716E6B6FD79E84F179053B89211B4CCBB07B6EEF354564CF51F9A823D515790ADDD4D1A82A2756C9D331902F3B1589D82EC0339648EA53DFC89970E83907CD4606F64AF034B15DE2AC5A5E052238B89DC44909ECFF199DC137F146122494E509CE000F549DF2BCCBE153E18280F86AC834493477340A7F0352392E580411DB8B9C63BDA11421BB103468398C3F253EC5BD4F914D5B8B31CCBDFC3C250CA1C65A9733B84D8C631675E50D176ED3A9B3B998BA998A94F03415C79533D3B091F63A538F434E8AAC6CE0F787B3044C298F90589CBE08E933925AD6DF055E881756FCA323234D5858AA438DABFA791AEBBEC34A5DE5705B820E86444E37DD0BDF7F780F1622396C92FEDA628DF606195E331CE6F06B316CB1E578F05C73A6F519DB8EACDA109E405BDEC0C517119B0EC33D8DEFD22889D92AE0BE2E2900E2DE66C0F2165B00C70C50F0E8102E76FFC6B7DD3BAE4D9C449B395E3B0BF0947A9FAECD5D349083B592CF7B85DD49E8CA89134D97BEE12E70CA2D315A4DC1404BBAFE8AA5A4E62D15E399CF6B942E1824D9FAFD37FFDF885ABEB3EA053C1E65CDDAA82574643D956DCD29EE1ECA71A99EB1BFB4E3CCF144DFDC5D13605B84220946E393B5D8C37DBE149959919B9805380A3FE1A22E6EBC7011016E142E73CF45AA0C9261515D741B95455BDBE800F286403CB7087FE511F9A03F5BB8AE5A33F4FEA640F980B648F3FEB0D1FD4709D1217A9C55E9C71AF14958B51C7617AB369B56ADCA25FC1C1574224C7FD5CD1CB6D8D37A6E2C1BF50ABCB70E0632E2268CB7BAEC5E06E2DEA9AF2F985AB3FDECA6B32A8981E14E983E083273C48A74DE5DFEA75A5B3FE474156D7DD7B33D513C1349289EADA8CA96792442F70AE35F5AE232B147225C911720BD8F228A46143CAF1A7556D29C659250E1240BD7F4CAF04E4AF9B7B1DFCAC0EA1BBC34085D5AF8FDA29AE5368479A7928BBB64084C94963F7332B435360BB7B8AB34D41562D4164A219C5D0C3B6FE5D9E7F1F0A83F8FFA835905663A46CC8C9AD04EFD88CD2A1596CE7FA0C66F3396C0FACBAE4A85140A5D40DD0B606ADC28BA94034BE683394F91DC8E42DDDCFA2DF4B3BE3527FA33418225726E80E0E0D6959D3D4A98F48ED161D27473F52E48D2C417CE5F5218CE90F09218CD3BE6323D20ED9ACE88FE3E027C3895FAB3B4ECD8EED7DB2E5A53EEA4E8021E03C58CD527DF7EA8339C6DEDE7D18245F5529248D7969955E829BA58B9625C9877629615DF31CC7E7F2D6933B1B74E27DA5261FC11BC15967977FF7255DFB4770A9A9F0086AA31B8F4E9AB0C61B7DC82A292D622C716A53650DD938CFFF0DF1EEDF94C9699F6394D598E72143EEEC92C58514EFA44C9630A678CD791895945C76854D3DC8D7AB566D4110B9FEA056B78321E17FFFCDDC50D08BBCF9C521EC14EC65F7452A39BDC7D24136FA3E092BDEFB4C803EB741B003591923B079BE485A9A0CCAE1DBA0B0D545FB6A449B5CE44AB522C132A89437095494EA32830879F3EDD8490B6F1FA3AB43E5E121CB14F752978CC47C24A55D8708C752CD50F6E2D5EDF27ADEB3A451B5F233539F44405411ECC05B90E575030DB1BE2F2D142DC617EE4EBB7DB4336909EA79D5C9DF4A864D611A6845F94B7DB542810D3B1D33C11ACC9956F7BE0F2DC3FCA24CAABA71BE5C5B340AF158B1AE1DAB56FE046DE73255B524A1FF1C1F6325349193307B93FC4B476132CF900A838FD00113608ACF5629A2A57B5C62B6505C91945D7493887E15C531FC365ACBAA4422AFF92FACFB0E0890BD0244E37B442F0C15505616542B9ECEA6AB0E129DC37E8C6A477B2F445BB1396314C989FDF7F04710D9E5D34C777EABD9FA51043784760FA458496C5F784F3FC1A3C29947DC8BA20147824350873C17A68C12511FD14B2E181AE65FF9FEA6A6D570756DD1BEF59D74AECE74B21ADA2AA37D633A97082662B725D4C42407D1E25CC90A05A253D8B038E98BAC90996E0772022CBFB4895AA3F6CEFCB1BB44561F828B70C91FC974C175347E8CE0DF0491B0F022DBC878B403B89C3B5A997FC33BED721957C48DD3266F14246F4A2DE8730392FD72C7A40C903664634D826F216E96E06037C1ABC237FD6E8437C0297B2D5D8B371F1452FB9921731864B9C8A018DF753FB1D0B40987D774B4176F712717D6DE7E59232EC5AE620DC6E939DFBE54B67082F1C67C827EADFE16F28DD3D815521165B81A4ECF7F714FF7011A45C82D5EBD583C9E19291E385924A4F00CD298FCC82584D70F762641360C071ED1FA918CD957CE773AFB58E0C72C670BCE39CD0CE6F7D56637CB8B61400FDB3152A5EE16170593E64BA4390A82C5A72D7C30871D98A1368AC4781BED73018E037F9792B21681A91D26EC663FFFA1E646071589355F795F9C8D5A15447B2686266E0B77AD6D928AC130DF9CF2BB3F91CB15C4BC5BC5D5B642845C451404B2AD26FA55BC4367D2802F713386D58A2F63171B17EF58FC93D38594AB35A4015DEE13D592F21B61B0232502268008323B63976BDCE0DBD61F5341AAA7A7E2034D551F989962F5320350C1E0F851E3D9E19B51055EB2E05D6A24E9C819E299C47B29D11F68B77E362873E0417D7B6715269A4A405F8EEA95F8E6F9416D236AA46ABE11AEBF56C4DB1951BDDC5DA439D4B099F215076484F82C0E9A34C34890C2BDB4BBCDAA8648A299C1D4FEFA8341A7FF8054FF309B0FFE0F3D52791E51103470DF8A5B70F6E4430191A281A75C9FAF1AA0B82C85B3BD1E5D38EC499BACAC5DE81E187D974305B878633A5E246BC213CC98A29DC453CD6A70EB7C584DA8A9EB3085913EFD3133FD501ACCDB6A56FF9CB6492CF486BF4413893F8C5082CCB9B34AD8ABE77D258F4915D38F9315DAEF10D4A067A8FDA7250098796894D2C2EBF5A3F86B6AACB8CC32D2C4A054D9F7B3688B84CEEFA84B2824F01DA0AC4D69670ED08804FBCB9AFAC506AA3A25865B0E7C8AC5C32F3031C3C81B5675D6914CFFAF95368F08145A26C8782FD89143A6626A8EDDC3353AB822E0F1CFA625BF9B83D768A6218740D2ADE5A4A6B45F766C04A9D9B04AC80E4D2CF593ACA7EC4F5F33E7C1912AD1DAA219E154D1BC99FDB44E301F97681A12A52FADE47F660EED87B416B0A8E1FC4C103F3EB3E24E3A19F7F8EF79B3B2EC19187D319BF62176451644D4EC50DB448B2D22E2B3DF4D950E33B115DD03CD41761F663266541DCDD34B0093367CFC0E30929201E8335FB523CAEC552E3C7B0104BB1F817C84514D1CE2BCD1466884723B64FA899973185354F4D2827F6E7279FA918DAA033FDEE67307E68001D0925CCFE8DE66840CCF442CAE27222D2EB8EF05A07037410159020F3A76AA00859166EAD913B48351CA8938F899508413B14A482AF0B133EEA7C5EA3F13030939B8B4C313380649530E4F0552D3B54D304939624FAA40CC692DB14197A134CC7511CE0E19866C664BDF9B29AA45D57B099642A8B4CF63D3CA52CB3744DEDC0622FE721DA778762EEF66D60260B9BACE0C2B619A91DA594C5783C8F9E6B6637A77F5A416A67406311B300447239B867A5A3A205DB6BE4D53AF5B443A89BC4FA51FA6A57B9CC9FC982A00941E9165FD2CE99AB80AF39B7809AA730E5BB203F265F2637C2A7454D518762BB8F597E6771A7F427A448627D5A02DEF1B47DDB7551FB0DD57DB674F4311A63F2B2D58F8835CB3F19830956146465189D2439811D5C0531FDD8C1F5DF3114F3560EBD82C76C5FA19B530CB0DC3932B2242A28FA7834B59F01D12035B035E9CC37C31CE085C23CECA372623940DD45478F95EAA1B151B84CF2A65F550C14FD33FF67148582970525EC768DBC2D2EDBD5DAF0A44F85D5FEE6ADCB93DE926F90B9C2121D6B2A5522786124EE38F8569987E3B8E6AE3637848DE7E8F1E6B8ABB85612F6541CBCC964EB7908C6DCD891DE2065E68C4A1543AB3A51C7CEC13D3DF923555FDEFCCDD89CF2CDCEC6C5B3CF045BC22A21E55EC2BC7262269F74471C5C1B4C9B86B7FFC420ACEBF2F531492A02B76F07F4F4981DE76664881955D255FEBBD348297F3414C2B026E136557B91784C3E400996DFAFD785467890FE393C63263E98B73BDF3F0DE581E5B0E5A9554F63B5A6396900C87F8E53B7B94474882CD2926B090EB54DD94FBCEBF3399B0C495D5A5A86B879A09B2730A43DF91862F534621B49F9BF8B37B3AAAE57C1F6A12DBABB10A7CE68DEF9E04B12A3F724EEE998A7FAAB98C4F29C0F580297A2EFAF8E51E2A5DC845EA45F2D091C4D8125EFFAEFE4E282D5771BAA9577A3E309D8A6E29C974B147FD4EC003CD3F7FD128AC017BF45515655D3C6D7909F52AB191847261AB5565EF6847742A08C982AE3075EA92A1D3AE01B0B624B562D44BBC7339129082872B1CAE62C712084F59EFC13CEC0A1CDB890985EEA0AD2756CB04B5A5B85D2620A2144605C03EDE8D6164A9DC6742EC4995368CDE68C1405996B16827EE4F6AA93FE311AD4C79D4260D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+            Assert.True(Arrays.AreEqual(expected, attachedSig));
+
+            signer.Init(false, pubParams);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+        }
+        
+        [Test]
+        public void TestDeterministicSignature()
+        {
+            SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+            
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(Hex.Decode("7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4711E95F8A383854BA16A5DD3E25FF71D3"
+                + "061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"))};
+            
+            FixedSecureRandom random = new FixedSecureRandom(source);
+            
+            kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, SPHINCSPlusParameters.sha256_128f));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+            SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+            byte[] msg = Hex.Decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+            SPHINCSPlusSigner signer = new SPHINCSPlusSigner();
+
+            signer.Init(true, privParams);
+
+            byte[] sig = signer.GenerateSignature(msg);
+            byte[] attachedSig = Arrays.Concatenate(sig, msg);
+
+            byte[] expected = Hex.Decode("e8a1883716841afecbd6f9bd8648bbf851dcf054572943cdc02dc9664ae096d1975ab97b76f18af4bad13de35202c7bf6cb93e96ecc21ef7ccdc80de902f9d21ed2520913104bc5d2159f6816e537dd5f0f8b418acd330ff0f2301a0336270f510f32d3a76f751d9e0c79b181d9a08f6e5f2263adc8a310491c2a3d3fcf1e9e4bcafeb91f73c4c4c45a08ead5fae61998fad2149748a409f9494fb3cfd326df717f2d89052c182eff2177725cc29cf3ed2c24ddc1ff20cb964b7e11aaee0651dab5c9d1863485cc9ae75ef253e097106d0c82007ec05e3d58b9237dc1feb4aeadaaf3a91a8a9cb4ee610814aafcd487472a08e09f363b46c8dcb56f9389144270fa9bb5b92088173724cb48c94f82d07989e6d0ef3cc42b80b651a48d06de9ecd01a075f2455ca068899281599bd2e85654e35775d714c21c5c04ae95de62be52acee64bc5a0febb7e9378174b6d71ec75fd38a06f7cb2ec4351c42e65d7cf8070ce93aa3ded7877c9d76b357d74d1085fb70b169ac9250ed734c1059dc8d1b58d5f55c24f191cd9ea7b235a8321140640c92ac593fcd0665ef80ed40813441cc0b1e502faf8cfdf89a000f1380a9f0d8b41bc53bea9d62e553ea97122bf152d21e2d1de982a386e4907d87449e7770c7430fbb7d356b2b61ca4405ab066a7c16064123830da8fcfc652cd4f36737a58e144909785f790273b7c4308282c957ec4bc9538441a1594dd656325455ed92a0a388044ebfb18f4994003ab1ee2c9507a9723f933176580d9f4b160aa0de77a9fe264dba93863d59c158abe8b2c13a50ebdb274d1fc6a38f4637cebec0e6e1c96e8ef9b8a0b6d11d1eeeee0b3fc1b1a8f931c029b77fdaa62f31441c0ba73dd10e88dcd514104c2ddc8bbfc1d511b72291be2c72471bb1b2855d39d39134061c00037ececfd4026bacb72481f845cda0b480ad1b487e4a0cb6fba9d14eb381cb7632c91b8c99fd2018c715357292c7901161782dd00fa8115c8a888cf117722ae4f89b4dccf35b3449cceb6c416bd8a5a76e45eb54e5c6fb0be101ff45e1e3f5ce89dc4f3045e927a72457d1e6c267c0ec9b8645eddd06b0950c674e4180a51f2f0d85277ef687b2cf5419cb77af41591feb4e430a6fe0c9863f8150d500d83559c15fec21d804eefcaf092ddb1e0b784200b58727cf47d217ebde152b8a9ca392dad03eb7556d881ad46981dd30d6129d1cc4993d857e953c5a7f8ecd9cc4eb1eecb424bf9054948b7beb7b6d812e58a1070550dbd26a572e9a40e3de2a46ae62c8ae59904937b69ebe9b745097c62928048e93f6352f8be9654917b5719052122d23ec91f4b7880eaa3904db91202d11cc34692a6323b5cbf0dc9a62b9da9026f0ddd01c8b011696d67b5bdc08e8c3bc332a8d872cc540d5a9d9b5e79dfd11bbd012b580c6a7fe47a742f1e983bfd36e697a1657318039c8d323ded8758442fe6f39678cc03715adac630f244d42befd04ef33a4f7d9da9fd52bbb640a9d2394f2f963870917002399ae880ff6c1d45d05b3be1f961b6467f754e43373ab37611ed9641816d5b6b6a79e58a45afeed6d3c63ff0eb8f427820a36d12afd2e18c860367638c04edd7cfa7fdde76c28ed316f0201f9841c32646f9207bc4f032949316b3e98ccfdd4d4f90e9da10b6e2567c02194a32d36f0392ffc107027c0df8d7f778be8b421a0b44fa25a57fa9abfb277d87519b7b87cb31ffea177f87b9758d5dcb3e0e890db3b37da55d38985004ce04b7331b340b26774dce20a7029db2c3f1eed328ef99cc7de2a8d12d3ae540d156889729fdd86cb93d054997d202c369fc0397125a084b59af52a303b79e6e9f1b4a9d5b6640f7d70d5a1a96fc257b0b83d75ea2e29808ee98ebc4e75ba56959fbd2350c70ad9785c3dd01fa586c7bce84d0b5af37b38641877a7975472666a9da857642884e2cb89caa6e54c54f3979b93a42e9364e279686049914f5e3a38ee9665d2189dce6bee50df27f16e1b428703f2b7c4ef14e158b2d0ffee1d041edcd2be997946842a6033cd17f1353e2df201b65ab87ed0c61e1448eb16f4e034aa28e8a74ee7c3b0994a2005371691491483004391f796b08950fc1c4476cbf96ef95d76848ee8b43a0fb0ccf95e4477d5589ba7fcd8aa6e508010e609ae5c011f7e3da70ae9ac659adbd22dec4dfe0ae79306f1ae4a4274e0a40f9956ead784b1031b9a26e8a94b3affbae374ee863859765269cfda6b4e6e0123cebd0810bc02120f2cabaf8a34bae014da3f44d7278063ed7673437df405c59fbc9e956e4a1eb6ae46e61b972b41855bb5918dc1fe2266a1d650d6bb86b4c27e4eb3a4aec51a0387acb327fc2799b0c974b0e877bd10207ae5e0603d5c454b298fb50b45a8fe11f7d55a545d606e891547d9a42b4ab4241157ab259be231fdb75ee134bb091f8e8b22ce7f4cd2e8f2ea869d56fa3e6526f6be160f4f314b916d98bf5eb34f8f0227f527a8e1145ee507e8c8c0f181a3efa6615254e0f10a41da885a7e801a733c24a0fa24dea81705f62dcb3d05ead981739476c33b5e77d44ea85b2182749d29ae4cd98a18654979a5b087c3485bdbc0656b821d5a75555cf8ad190e140f38cf83d1337c207c89d5e315e2ba6eed8cb005168de6422512a4bbc6e862a97760c4e8c712f7971d2b416dd5fa016194d28b785367b3607f3d1c3523035e964cb28aea7f32c09a1da78a876b643ad0fd24d4a7255a23aaed45ecc540beb7bf2cc371a2e13c4e33fe84fba9b607365126c6b75270757e513a8a5b5910b24c2b43bd7d93be42563430be20641462b9306491bc0ebc467f3553cdd1e1b9b1c8940bf60eff7a58d2ef4688edc77ea677481c028de4511209c5a845beb66d75a7b5bf7505f99fde0e3d64cff1fb69adb85077e4c177b7908de5e1fae70ce3f335575fb5d52eb07c96d9f3cf84eb4fa8d709026e837d0b17aef401c5dbfd5157985fb52b9358e8bf2f0ee2d05ea15b633df2154e7ebaacb6184324faa0136d521d9d06f4b8ce2dea422cbe5ab6ba3e7efa2f2c921ffe6818f71d7e7ac79706d7eec35821b5cc3d6819e5b08ab356a1cde0e7eb61de9e64931ef54c181b46cf1938f6c5176819b3262f34d0d767faf39f8642dee2ebace43763a09283" +
+                "b79cef58b2c94acbab1b97d224b71a6e398cd193becd2b9485e6a504c75ccfc7aa7ee472b111b78eade4b169b518cc4dc69d7347284f09539131fabe0e82a667c53f661670c558775cab483800294d78904d8d65abc1b8838f5795658934805790f56dddb0aff336b12698806e375fb15549820a2e1aa627297ac382e789d8e05c35bc0a2b973f890ef39d5752f9ccac4010bf29d4f56b5d9fb188b4ecfcbc0603ff8d23b74ff4fd5c96a37581e1fc1b4d5d599ec602d5890d5b35757e0403817c430ccbb3e700e660d76bab51cd2f96ccc0a8c1b28515ea966c67acca36265dcbc44080a56ec610d15cc274f6b8f3bc3d9a7d4b1c2475f7baaabff3288817c77f10da84c9c400611beb9077047aa05bdc1701870cda7c5e3bac4b789cd9a8e4a05ca27f7d5a4a669faa14e6a4e21bb7541ef030f735a33c04889dd5252a54ebad18dfabf398ef3fea693aa46af12f5671cf7e032aa558288797c4d8173f8d18a8364cbaf7a17b7031a97909368dbeabf9b7cb6338b7879ce000d912f9745580ecf7478fecd38babc9aa1529d7e191b4506559188a3525d93861724318a4a17f9377f799358f502e54fc402d32407e1d5b2f40611660af5affe47b92343b3207b54347a4a4a3ee4a71b0004d0dde7f4ac6bdae5759b994c2388436eb47afaaef694a7c77f2b4399177940cc4b534a5f524c97555fe101a2818842fdde0df24c01c1f07bba6a403e3a53ccf7f4bec3369e034c97b7cc0f3f520691899579486af481ebc7858181ece98093b6bbf9fbdfc7c721f9bfe7d181c85493f861bc9b2f8795dbad1348d2e8a0c6fd67674ce232f098491b12d1f1d6726a5d275ff52f5f259336309360cda2b0f450bfa9686f76de5e7ad9ac23b7e9785713d0ed46fd41f7c96c99cb684c4137bf9a3a4570c6999014b42538b1ab8704b5c2cf9aabab2e37ad80f963d6e00821e55feb7ef22ce3f7037efc38b735cbe8c52d7f4847652c13074e6864504f5370414d19500169b1b0934a7a51860bdb9b91904b70a8c3dfef84fdadb6dca6ef92da0c79b10fdcd4372ea33fdc0f434927cb66b1b8663cf8fa455824b71573d068efdba6239021d66fb666ee66e95dc0efd7be727e6ed1976a903bdac2ffb7fc7fc98798db4bdab2d6c3aab572f8b77792e0fd9993d1743b25da43b1b1ae06db30330c54e07b7edbd387753d8f32ba253281b345cedace071f05c82850562b6d256e493fbbcdd598437f3c193a46ad201c673cc8d8aaf2b3d9ec6601120699e747b1fc85e62efe188f0d943f8d8a30e90214ea4b9c0cb0e89f94ca0bfd7157d75f76eef56550287d9ac900f5890c2f95c062375c499c1d55e58078396fe9a65f72ddb27d51df4ef23ed8ee4889da78149e8a46f16b2e5f57d602a5015280b3b100a472d7bbf68995dd21da7300eef386d05c7319910b817723ca6ee23e5a7da60ef5067006e66f021bf0d16c68fd074b555da462db2422414c5683ae2b2923fd880593d0ae32b1fb35326417013373bfa4c5f83104e07069a99176741b7d7be79b5cf3f07daece2a3bc62557e35dfca0a0701212689aacfb5987ea9aed3a796db6568b3955797b3b022d6b46e0fac18f40c89f1d3d1f92f7abe77e8d370499d5eb36624b9cee144da18bb0755d39f40fa9beef1e7f82db68b364606bb8a30d4445e28d3de76ca03a23379e75f276de8b7ba3e39c3dd46cdef427070f542bce2785f6b555c1a65e362211f67ded39052a504f16ba3e4d8999b6b68efc4e6bf0e812ff222bccfe34babd96532e102faccf4744fe81d14cd900698a877a1bc0fdcdaea38d2878551e1a081ba333fde860c006d9269f5a9a013ad485cd288633a5b298acd430bc030961e2025f7d5c83fed7d46d90bfce159c7effc872141688dfa50370f245afdc45afb159cd03cb4ae6ed09d264970cb498d32e08c112da173fe54c978e8493d6d28f219854fdf8e451744a62f06e6d247d583361da24ea13c2ef6f54caff95267a82a4c7419f46897d3285cbb0783f4a5cebfb1c8df42e516d3664f5d660f4c5c7f798f1968460d670389e836454abbb05c41537956a937e6e845a2ce37a1d539c9c6543206e9d65759b6bfe90c0e5a0797065f01b8bd15ab65d7af6f0f3053939e6481715409c2c3cf9ff273fb7d49f99b29e17239be3d4340906a0192a31f96dd792f30ebed42fb6124cf9356b6084d122f3eed7fd8cd1ea04997d9cd51c8c61ef0c5f593b3230540d24c52c42ff0678f97ade8f59a5d0cbc083bd3b11a13b7b399e483f73ac2cf98ad26656fe169a18f1d319d1cdd66903071ae80d7be1dc0e1a1a5132a624f4995417b59ad6d71e5a08d8722b43500f675df7623e54495ff8044de03af74b10fa26db4a8098d740ada5aebc43dd43ce0d9d9ee7d17adde7415a52c364143852ad26dc34f4ffe9257ac0a82a90db97037347ab601e97a2372f87b0925629bcddade9c6625c0e9875d3054dba524c581c3785be32992b94dd812cbe8aafc5416136eabacfdb4f311292d67f1acf459f99f0670af5581f3036dceac2322df5a0bc1cce7279912d20f9638421f1b989c1402ad2669bcfa50c77fbabeb40bd28dabbbcb72f1889498569d304269dca8374f61038a3715b38552f92fec649b6795ce692e441bb7e215c5628d4bdbef3e81054be6c7b89e7137cf17ccd45c83dc9710a0dbc50753c3bbaf15f8a90e406790be4db8f528be29f090b5d9908f223658a4a37f03b4fc53fca4119eeb2d1a712c11e7e4b75f41fd59944cae1ab2c6d01a3b9bdee689301ce9cda1cde0dabf97d78bfcb60ff0033c5d9034f9a8f50145752840a949f2ddf255c0735a1f5968b79a3b2436c2e5768f935bb6a58f41be143c9da1a21005c528b0daa3e9c60231deb95ad0fa4d50e1ae4f6f5c508dbd1260318145f6f86d7b828acbea3db71a9a9481b958c600ccdd8f47d3dc1a671cff034c0654d401151715edb38a982e2fc95e9a12b9045a8426969d40a5f94b11afd1ec742c508fc3b6cb783a5a1b530af8176d910e965671b9060b8b15903becb7b1cc94efd5aa8d8fc81bf704e37da1e6727877acc55d12dedc5f830d0b55371b727ee84ddf0999c2ffe695c98d0a5c0712357792fdd80c9796486d23a42ba46681fc667989941014ba48c2e443bd1a176c61733899d65273a853028eb5ff34b08b0d4fe233e230288cde6a7b7067591aaf3340f9f964b6b06c73787d9337f926cc4f26632623ca669d73f2269a6a0c336502417e9a4148fee3f884c20cbd8ca700f4b41ded93fd84f7f9fae41fd20a5604b52f018df285deac2bf0cc20c2701848436bc244a3111e07032e2e43724ffc1a959a7a7971dd9891f0cfae55e1ea9448ad8724650f383bf9241011d37cfd7ec298100ab0406924203b0582c832e64ac13b4a69dc528286387cd77688c9230b75f8862cad010bea4725952f716b694f72ffeb9dbfb89ba172f0117792083b44c81cee66d9a7126d99cd89ab5e9e03f0e23550d37a88bee39339da141dfe27f1a9a4ae1c07d0c0123527df19a45effff06b7360b1cbf96aadfe2b28ae46a9b1a8370ae41c5c3615112d61b0040480b57e53bde2646580d9efcc36e54310768c57e266b48965d8dd7c45a433b961949e5b0c85dc40e780b26f53c9ed353a6ee21d9f6a3ff5616c48778cca85484498b9857a0b949a0fb08027f8a0552cb1a3225e17dff2624834453687977f50bd60abf954476c319fc2009f57466eb00e59795143a0b1544bcca22a6a893b438a83d2a80ffb918cd3b2fa7e6fd2a78b03eef8600fa980d92951b75dd20c9792117477f2274d0387588d3b452c76a2cb0477842cfbd4910d7eeb8d8097845239e04b1de49fbdcc09d35e8d47fc447bfc397eefb6f1fdc61c2bbc8e1c655496a2d694a1f8deeb9b3e08331828c9c054b204716c1a5c8504af13cd9bb6379368eaba8cc34d529fbd9f7db177ca46db89cb7259d305c80b205b4337b0514fb60e08d33ceda34c4a4d51429a12b808c46f63ddfa349b177cf8c8f4488464a22940b2f17dbe5f852db4640597ff198f7b5feba22888de7bea2485887ac56f54a68410fbb6ffc1a39f98e9a155059daa88bb46e6c86ec4bd4f049c8807ca10385cd6fccce5a64b84f999b2aaef69c230734e2066b6f8bec19f345e5e7dd1c0ce56e508b7f10f19cb1fb7ba5faff7e152aca87d65d9c6a89d03300bc0723906853d036ab50d371122b5c772c435ea1b20fc8b1ab1f7953afd1d29d3a9c53add9bce2684956a5314379fd3030a0dfa9e81e6da253a43b3a56b7cd9bdf3473714a052e0fc5db75acc502ef409f6cc267669d52576c14462f8dacaec67b6d646eb3eda205d794f2e8ed34dd2ad2501b87cbe07b1dfe0a9705fa5113c024187b996ba758d915b1c1a3aa8ae76d45700b4b91c42029c80cf470f2b1f48d4e0a9e02231eaa0ea21a9a0812344a79eb329cde74de5e85bba7730921d75d09ecd70f66215f369bd24dfbe8f539c5fafa31ca7734cd8281c6ea7accf9904581f03d44c3498ad26417ed65273ce8ec7565cb86348b01ebe72e4c4244ce853004dce3a729f59113993ed64ba814e8368f1c16d0bad4df4d2002b660a4776547f6151e4cdfc9fc075993f446ddddfa824c77b5cdd0d9147d77530b1bd917651d6e9b325e88da6e6187ae3b4ce32092063c0b838def17e56b0fe326d1d0e1b56488b90c7e13f2db0d96010071042a2332ea8a423f398d8b04b768eb79ca68cc22f43c335b2dd6148fceb20c367742c00765c417705d3786063c5f23785bc34f48a4ae600e7c7963c53e2982dc889291f0915395b1573015e026dd1ae9ca39dfb9a2d2e73e16a4e408c34bbd99f727725b77cfbf5f2825f95a141a770221909c427d59e9264aeea617d0bf948ac3f3b3d77a590c0b44d76127cbf08a9a2514317813d55572fe7bfc8c27118c9f0536577ec259b866e6c084bc324a5e97df2fafdbfe060ae1dfc2548eb167febb076e3babc0f705a4a1b85e8873c142719eadc4ba9bd10c3015f6a1ac5f349288f43ddf34edecfdfe24522be76307e49b266a5dd3ecd9c45ce9fff1b0b9e3bd1877b7f0baaa8b2051316570fe1deda3f5b52c54116b5b6e64c316f5bc906c43a747e477f3e75cfd0ffd80613e2df98c0203b55a1e071ac870366a3cb217589f8c11b4f5405ae0f9ea6cc6956ec628b30b2b890c300f1bba21e07c63d8a4030cc913fca60318db89812bb8b19c0b78f5d3fd9358bff17502c7fcbc9b9a840390a0e81226a4516ebba7a1928240949a915b34d5af2076fe43528411584bd70112ec13002817e2d2c3c2cf12c2fc29f6b803d04914bf9a6c911a512630e4af0462b5d02d11aaaf9204b92f74e3db0d941d9bc0fa9a9ba0db2a221cda737c7d5edc90a92be700883551f752443a41ebc2c6d92a227e174af751148f88efd74a1a3e9b5113b5b77b9ae185c1a1edb067b2057744c51b2f6b733cf6343fc3f93de314e558fddd52b33935d117d7e77b4053153c4624f853ca65023ab7a68fade2ca474c00431a0aa679ea2c63f93d78b0a0dbbb06037edfa4f6ea1996c075e062c431c21cdf6c4633585e5288af10d6bc1b90c57091713efc143b59eeb631dd326eb5b89d92a63384fe342f49c2b051eb68d88ec5e784a9d241db78c7306c260b0de1740a7cc21ebd0a84b21ec17779beffafc82775503b2cbf7a1e7996925abd5fff3b4caecf8332942bd5e75f34a3570a3e02c181b93374883efca147665584783a3e18467c9a27af84a5d48f8a0d9db57158364a0344f5ed4d787b07ab21085f6114ad8bacf76b506759751162aa584518672186877fd0885a8c35525734357b02756504c85b9765ea3f6964a20516b5757fa245c2cfe9f1bc0e9a1ad36309eb243c2229c1fe732ea9bfd36c8e183996ce629eec480cdbbaa4703fe8962c91585e27e9976ea6c620979b6db8e75f2c3f18dc5e09f9e4f9f615ac9274868c192c5040f066f870262f2e41f1510e3327f5aceeffa025024faa5b6cb6b83aa1e4fb87766f94a7586700f9cd635915f91f9fe0540322ee96ab373e2e0c5b8ff2624a387e1d8e20380d4615d4dd0a7cd6568c4ac8fe5b3da2deb69f88e77c52aa0f3225cca620e44c407d2d8feeaf367520529793c4d7f82c497e01dea8f5cdeb6b642b6835dbbf1886badc0a82fdefcb489f0c5233ecfd981f9bda484cd07270885493bd61d651c1c563f9e3f8ba70ba686d5b3dda87b22ab92a9861f386220f76616f37ceebd33246ff7b5127db5ab499ba75d6c756d3e436a49c812cd65b5830ca4932b43a25fcb5c73621c4c6c551b9a6d250af278d2f7ea1a204caf6693b945f34f4a00255523dc150e540d07253cf43a1bafc75f6b72c71697cc11f004badb7f27371d8b1c193e88fbe48675ad43d8a255094f214e867cc34fdf0565635309a0cebfc8122c3f1b9f47175f1e1543174adab2cbc8a89eef229eeeb80d4a83f901d45997b8a72c4aac73e40ad7762aabecf2a229334c3c8977650496f45f56c4fcddb35392dc92c055b9112f2f5b905abf4dc10595c97a080b6cb2dc82e8ce4a79eacd2d75d783a5aeb645cb4ea4a118da0ca1c920d83662b7cd151286be786bd48b93132c464d9561d60595067b805b687f5942c50523b7b6ba938b2bdb138e1906ede55b195b6868cde422b5d838fe1118a79a966a04331dba67a80545af37e5cefd38bdb5076f8a5bd08ae170c5db18e32afebcd9d0261815bdce030c5a0bff4afa360163692998369dd6f5c460679fb27ca7707b4869e42a432fdd961bb1f8075235cdbfffb4d0584cb2c22855b10023f0edc61acabd42665f6c48bca3f04e60f1f39c8c882f3b7973bcc81dbe152927f4d95f9450eb8e5a53ca9827dcc51a24c750fc45ba0b567beaf41eadadd5dc3938330af0bf58f7e5b076719637f70d7a0d1d89a1ddd4f37485bd6b20c3cc58e72696e7c632c5fd1d7ad2ca23791dd2075de18e3d67a6c43601484ca13757262188be6388086caefd186380032380554914eb2739d96acfd66d081158039624b3f3b89249074812727fd1f0eac628f23e55122cf210b50ba8b9098dc5ae9a6f6b439788a57b525c8b7eba6fd56234e9d18657ddc7f36ea8da19ec27c6f0962fbded4fc0ef78833125d92b5cca13a64bb006663f5ad104c0395a75f789d88da80e42b47c6bce43e7c8dcedba326d654d6166ee4ba02537c0e844cf89804c302c688eb4be46bc3986aabf8f35d9d433798aec7398711f9ae414c675adff3ef37267f162f955bd147cadfcade61f4e3911faf615f0191ddab8fc64bcecd972f3bc20755c2b49edefdfd7d9398522696209f67f39566e96b5c960f254f862e1ae88cf327e8064eaf6e55898263986c57351c4632c0f885dc8950c9e1b2e7f63b0e866e6aa6e0e563d693165a782dfa97fed15c57735b3540652bc516f9501fb411be95576a1a443bde64bcbf47212d07f6bd8106e9f97a1ef45fd645c9940eca6fc9bedee9217989b579b4990bf790fb18bbc5f31199c99195b2331f68045d89cc3a554e5c17117913591e70386297d7012d1f61b4ddde16ef7e1b57246591a2f7e3651c5275bfb6dac3ad1a55ad2bf363b84ce93fad8a9cb132a1af2ebfcd3f9f112232e5de8b05268352558bcf950fc8f4868cdb4e894764b60ab0507f5926b0429231c22d3e8dcb49404095500ae61b319daf4e27ac9b691e809d441c98b2f0ab1f9e7f99175b70bb20bff0f8a6dcc7233df848ee723d07ec3b72d157d6d3a2bc2f53914b72a1a8073e96c5cb11493f21ea195f56c0afe29ccaea5b617f0529f357c39b39d96bd31a811c190d6aef0fb222e400071b4f831803c4dea0fb090e829b371663a2417d7d9e4b25e66b06d56c6852684dc16606276841ebe9a340d65a1fb419541e9ea401053146213e2093045f51714018f4015cb398f6a96648425be44abe16d3a6b64db63e0739d4fb65280c0efcdec27e7c0130cb120047528c912b036f5ab18fd6dfa50c4ff114a9bfdec450401f997a470dd0eef58655025f47d45a4b52154082d44ebfc3255e2e81ca4f5aa600cf807ba7b761938cce8fcad74332a48effc8707cc7ebafdd708592d0fcb5005695de71975deb9f24c1847c24f7af58956bbc1d4b1f216ef635cbc78f352e71e3c19fc0483dbe51e4d5073fc2ef3f18c0868417db37386c5d03d5adb28c227f667d26bf86f3de79caf4139c6c4c6b2874e5deeecdf31acf984c4c2fc5e052e88e8515b7c3875737544370b273e53c756bffdfec54c40fc82469851399d56432060d02d500996473bedc05fb8028f4ffdf185e1b565bfc0da9325eac3fd7b7375bfc061e0ec02c16e858a987eea418c6db513f06fa002bb938665bab851cc432db2607a17f3c0b4cb28ec856747a1c1f6a1a9645b6a650542345a7988d81f64e26f8e610f79bfb8bf98f24444ac421567ed20015bc5cc889606f559ea3471b085a5fa9f61443e94cf5b87883274102fd19209573244804285a9b3856de75ce42218b216c9e45c3f5fa3e2b95d1b46a44cc6e328269b068137f37ae44b702128eee5d52a8bd870774ad109790238b5db4ef25d1de1a8210889d537e58722688cb5c76c05f1dc67c2ce1748dc626145764c3085a44440626f60f44c3433574c88d3468fa23820983b0c234559e1d87fe4a057f136d79fa1c43dc20569b05c58ec8c507ad7f708b0cb65c00e5a49b2a87535de183ebf693c554006a52e6038c6b0fb8db16494e5c0a365a0bb7b4b123b77b53c7bc88250d91eb3b91a229e1d8baa01bd594261b1220f8600770d91622d59ccf613d7566610fcfb31644462e6abdedc9c7e14b423cff130c21119c7c5b38ed75efd6b00eb0980a77d66ed2eedb3bb1e668eb2ba2813c6b7436d2de03ca319e2e78efaa6fdedcaea4446fc26cd1f6216056e198e30532bd11b6724019a9bd611b3374faca8d630ddf39d54f58e0c1929e7035fdfb4f624fd54b78074ae5a52ae4f0ed7e5e6640d2f4658832871289b84af73b5d6c9e920803b82aa8c0b96bff701ad64a329e1fdf2661ef7dedc0dcae9d7593af2e48ac64818d176df8e9d4d9b3fb3f05627cc9932ca7c9e112b10b5bbda5df5fe6d64a580a454eb77592c0656278d27c8be40ff2bd6235c48157336480f4b69008c89e7a81e67fd146fef949114b75a806f899815b10c75ea7caa095e1188d18348507586b96486095f0ead66a67b1d33868714235ab42abeb4e903b0330d352d1870728e154d8433f03a97b0bcf411ea74541a31b7bd9a439251f74a57fb00f98a1ecf1d7a0f114707e352e96b81e901cfe5f33d190aee198059d500b435a7a2cc0ab54af6f71f3ce208969c480a9adffe4c71439670ee779b6dd963c9e5092560162994b55741cd361a71e95d449110be7ca7155d5b2fb5642eab14d78e26dfb74650d962010430660efbd72a6f30c95debc9ef54f073265b07531873acd0407f62f021fc621e7c2ff42e9be454b313c20ff7be10d30256fb59ea3a00c2fc9714cc9b60ba45c635836de720bce6212104e71950521d761306178941061cb1ae02e0107f78aa9a41618dc2c89b24a6057f4aed38d6a960b8e9093cbd63aeb5a11d36dcec93d965f978c42e3d874e267b883ec5c508836f4ef982e4914c72d1a7e92e0707e1e9068aa8c3f303d102696ea006002fa2b671f1cf4adddf0aef381a526ec4be4272fc1119c2e00c2d7222fcdca41e0dfda7e57de86c87e3e7a43b4337fc43ebfcd20b7aa3e323ec93f05bb84df02e4ed67b92fc8531023aedba11790e47cb8b191ba026d3f25366235cf24965f08b7a0c2b65b1be99c894ea35b931fa39bca7eebe96857e22f77562551187acaa450347171579d705f681b76f39c8243140c59ff3d26fab68eda2acd4d44b38322291d31bb32f8edf0adf40bab0cbd8848bc6a6c0ad7fe31128a33f434dc82c675c1439e08f2a18bd18ec3d0c7b3de0cd3880bb98cb6a2f9ec99bc1a8ba0ac19218567481589d16ad4ca2b5664ae3b54376569258d76291f12dbf3a74f28e890618f929dc9a37f9e7913741ebfb024c7cc85061e9a731b6d770386bce02ae10f6df9ac23c0d174a9c766823b825c91252f631070be7e4a799fb608d21928dae8462b2fe6a5bbcc82a95ba5f42f8dded113fb4088b8c076767b8aac5cfd7f243b44015be98831d8ba531075cf1ad712cc607fc1ace10de39df2fe8d83710e8c7990710baee775386857141f289e16880cf9910ad5ecfbbef854055404503caedf237489b92b47ae5071aa30a5556a38855aeb58860a7f517e6c7c63a53ab513f6b8c292743a5cbe1053a615b5f031311792be734cbfb611303626265847d6c54bab005b09280ffaa715b58d5592847b990a2111fd57e07597afafcbc6b82ce2d5c4220ff70db53fcaa44ffee4a20726515d8a2211f2036319f685a8d844bfbe25f810b5ee2ce504951229a4a453c821e2de8fc83f6feb6b614e4ebffde6cbc8485c3ac9ec46221fcf09cd71aead885a07a60b97bf9efe65c2d18fe7beb7834651939d463b1d63e4c1d28ecd4d3cc777de5144bc97cb3cc5ff10824a79d9a6734b1deaa9976760edeb20105a14fb7900a864e1a8c5bd57c1ceed327ed813baa321809bc050eaf40a6a0e2fe95d04c5a0f98545fa10715cba741267f4067bf291ac35462e9534f7f16e3fd0e70578073994cd2826ef22cd2dd5e1985c5236bff6bcf2cdca7c21da3462b1d4d65185696e1c34457610775dc4f0625612db915bc06302af1e79c30dd988ceb9a99ff08abffd579baa4f877183a9986d21b10c5815ab33132f53e61858507194766867f020ed30d8f8710d065f160ccd1db901b04046a8746f52555de5fcb680c08defa8e177094cd7d82f33576dd1b237a5662e10e16ed087032ca31ac4bc6d9585f3d388c61bc2875d134d505872cefa2c39f70118323b984b4cc5b56d9cc8368104bcc47f550811c8754cc1967357fbc1314f98e17b415281213b35c7af687be267432f06dcedc6914060d4489daa38e2ea3171aa57e5b5b1ac42acb8420ecc776093a00b1e68b49f93ce632965714aa5a4409f1357ca43e3e84c1e40f92a3eccbec2edc14b0374979258182696abed13d65cde24b1f9255d406e914e02977cf6e227114af6729c73df91c88bf544fd50e2f9ec2f4d17bc458b780556b6dee9a481b3cdd47798cc5387fcb5fb43ac5503f176c608671e09087d44977e6e87834a40b2838b459dff89fd8e001c9199df040c09b60afe2e60900de7deb6455b07cf22caa61394504edcbf2145f2ffaca5ce1a032f453390567a5104da1893cbce7e6509cfdd53cc25066ac6dec5285ce1eeeda5558942eb62e001bd274264d3c785362c34c6b6eafa44a418c6cafe70338de0580783a5f070bd8b0cdf26b68dd002a050502e427e9a29b5ed155e06fabc7a8960862ee6bb04948cd6ba47f3bc3e3bbde4e65b227efa7696dd7dc068acbe1fe210ce9eb4cd79f0ac01d8e0b48e89772f2c30506b2087ade6d186fb4067069e6a90dde74ca3f85f1744066c913991ea434b377738189daa7ff5b7b2ffad933f2d4d083b413dcfb93ac95f6e3e1cc01d06ac50dca6df88bdcb61ca8eddcd2fe9a3e9a28814ac7d9c3b23251751b38c427ce8b978602f1a20cd32ec7422c17a8ab60d005b78cc3326747ff20c038bbe794d0d86d049ba729573a03e0d0bdc87da0b51ec7e91dcbf585cd9c78494097ff11d13325ae8603a90ac7a02d3fe1568fe71a1fa738fc8590353af554579640a9a6bfd298e96c97c87a7df2b97e00d97e8a8a8af7f61abc070ad7716870dec683aa574c56d599afd8bb187dd2d6f4610abc4b9ae34b8e2172025bbd6a41f9707c5d89ed7cfe5dd3069a5d4f8a54e3a748ee0b20ea85b3c5d9bbff277bb1cba6b302e01e09921a1c00a435829f2cc6283454c655bb60f50e1925fb918859fd0e298444fdc70f50154dd8a2593ee385c03972222a6712d8377377f470216ae30a387f8e3529474b94905c65a06a2733915ae55fa188cb0bd0301ba3fcc72f468569a16b04916e2f31746ee72b985de1c9e19cabb06ca46f1de7765b10146aeb60dd107f7922243999f02505198fa91f6f9e92f0a0f8c18a8c67c2d6bde70361420f97525a74fefbac297a9e08780060084c101317894ec247722484e488f78badcd909b08a322b734e1509f406a3616f25b7dedfc351da67fc29bd9f661f86728153789c2b47b52fd5a5ee3e0d7589cdf681eab6b1190bb89695b3a10c0e8988d0580fa39a080e036a4efe1be4e195313109ac2d29d2db3973abed753e6ee6198914a119298bc19ae6438108aaf3c61c7a96a2b7276aa3f32161f33661acf0cdd5c21a562cd83245840796c50436fa8c3acf59ac874ee0ac60a25e284cf04275016b391e2172f418ca0dba39793deda4a6050a3cf0dad3b477aa45bf34746bcda9c4835a5cee6799d0c53dd6af05fa8765168d7d179a55a0548d36bf5635bda512ae6befe0ec14d952a95af6cdbcf6465c03cf180ae7c3114bfcf0279d957f60ab089a4239131d6bd535ee60b909e71a30dd0119e94967eb89f592037ac1a3bf281745fec0e27219bcb523b29412672fb0e0c306ec339326e13d5d8dc5e3cac98ea0d8e458a8994371dfa4f6fb392a57f24a4dd30db0b1d743da747122e56bd5c93b707fe6ef95e424335fee87f165e8459a3e38886e334de295a37f2e22730b4f78bb69ad79b9c9f4f3e7c252c9f85e0292079420308b5f58c21bb048b10f27a6989ce8e1dea7eecfa46767078eb18532fd6b8872e27c7cbd1d467b4268077aad1e762ab4c81e15f5c1eec44d961f47dc3851c2d3ac60f5eba379f3293e00bbaa4544e9a084077c2d397532eb74407bd1d1df847c01a355759d26e24dd981d89a4d459fdb762bd50c00973759b3b078b9c1fa35f053b3c6152116e8c9f469dfdcdd5ce0d171d7cc0696ea31a7ee71963d41d189f93364f996306cc99248fa3229626fc25051cad98be834eb4922c6009a83699cded08180e7fc47f83b3c6acc6edf581a966f8a5cec53f7dff430b090a68bf315c300bd5d8c76a8a39c4cf3bac58129d4b35b8872e3b8de12723dd1d9111a8b2d1400973d248a104c8953fd9eb5ec74d13def2a4201a9ea594f308c8aeeb745bd422a56a7010d427d2e0c484764481717dc04af742c10668191396a919a02c79ea1a225e2d346a5b7190259b948e3da43a31ae3035c9a3a02067b7a341f64047ef22d374741e2f7ff3dcb5d79a991d9eb2408bad0536e96a3a0955bfad65824fa1d8f47e17d1d27b79e1d4b2322b49781eb0d8286f5a084bb24d45778b33bfdba317021e7bdc4007b9a95d44f5584bea9d881299c41fcb14afaa49199773fbcc4d943627d2b704713c4c19d787bd42c5edee3ee4e99911baaef85a01b530db7e7148db012d6875d230fe5ac63f8108f496a6dc637275a87b965046b724a3138204f95723f54679d3f755e67968d34a45bde27ef8a94fdb9fc669b4f54ededd4b5736c1ce0bdd2dacc8a530765ff9fa547b18e844037a3c7b23cd2081cf5cf3ff1ed2b21921ba24f46b49d11eaa1a4bd9fdc6a5b7a57c6814b8a5fbddd4527edddd37ede32493ee652df4a9532b5304dc24eeb5cc5abf5b450cf67e19fc7a935e9813b6bba12d191a32067416fd12bf82b8197dcab44296e344575d5c4aecb7d84093f453311c213bdf9357de2924fdfc58481c134e87f7e55b8bf8ea8eda5739e641cafabd38613dc4e74dd78caed72cea43ee53a12d0c6d76fed8822b32ae34260417372dba4566f095c40d34d91c2a05246fd6ed22972385da8a0bf1b1f074a0c99437844878a45fcd2dbbd6a187c750203fa860c1a967beb9f0191b88ba90c01b66aead6118a0264498b54bb71f366f33c667d0a1332d4da1f6d03dda1299ddde1366c828456b40ccb1bc9ac012d1e9805ec4195dc3a3e37487bf5c4dc341d42d6afc7887998a2a91a7507901f4a498d202adcd601574906baaac4447a34300e50f6ee276cd0ab6ddb03bcb4eac0ebd2301a454192fa61f36e8f7563cfde81ae54375b7f22dc458cb3384bb3cb1f79cb0ce67543ad34fef029c8c5637c499569baeb96625dcf7cf3aa21a3c64c34da380ce776311afac8b25696d3af113031b8e773dfa607883d27a93a3128f91d21139fa24697c2c309dadec570b6c844727389dc42690672c4da61a832626aac34f03039bfe455001ed4131a5e8742c3e689747f1741d2acd360f560edd7cc5d90185f8536c5221be675e7f2fb8b401845fa67897cb07ab7fa47ca0dfcbf32f38a3c3ea18bcd2bb2f9f526e625feea0a7d7f6cc6543eff03b4ae13d0ca4eaa2bf3b63c59cb26cd79955c62525eedea7a505c148fa8ec39e03b1d7d7b5510ba2bc120b1368aa6d322d16a047e09b6d2dcbd1e13a7e1a020bea339ac745ce19979163f3c949087564dcd05bb7abebe6db7dfdef67f767e16f30b7e3c4291975558ddb77d3dbbe9becb61d32414d92f814ea6575e3c0ec73341212061ef9e5768790605138e18b92785ee4c0b49c3aba0d2aec38c165744f1110a3fd9da386d7b768491d0898a797112bd3c327c07909daab4bf07c2e59b14e91c09e6872df4714d1b2cec69955e74977095b56dd6b04f54a722e095df4345b24a0e68f8276c9ca38dbea0fa33604491fd63d2e705532e0a1376584f22e553d770d08de8777d33e6d90785fc9c52d8157a3f30b6143d7566104056a4f9d83d0273ec436c99478286f0726923fdc44cb3eef4841e519ebb3113887addefff2f4cf5203e4faefd59feeedae1b46358ab31e53e9c659d681ea0ec5436bf7c31ad92d3d55ed2f5568210efa2d0eb8c8e18eac07676f42dafa1b3644ad3d1c6499a008232ed20043620920cd77b89b768b629076bede954b8baf1a27f5c86e6681daf02b516516eb3fc9ba1dbfb54987d8bf57c1e7c5fabce24f896093cd29a79c14d5490d98ce6cbe56eaa90eb87124adda2de00d3cb1519b331e7bef7e3f277474cc9892cbf0b4d5b4d8fc842cd109ef0a869f2a0ebe299e16007ca40d3084392e79f1694a19f75947209ea99a9467578bff1cbd1a431f7811ff8b3f313e34ff7e394eaa19073c6bc2e3a1bbff638fea5f4c3585a3c2c55556e7c4bd476197ac85d8388a3a9fa237522d5c064c9feb83eb9e41bd2ab9a0d23eddf40afedc7ea3580dd03dde9bdcbd7ff69fe3af039db9e62d80eb3c2cffd1277cf066489ea12b39ff51beba8e41608be3ae28e861ff408c2d28a4965c1b60440dde5c0e2b667cf9b21e40052ddf1fcf8e8799dcfb2bd0bfa16ccab599b28f6701043818754a1d10a572c8349187498ff7b890fe4fa9bfd0463b2d7c8154849831c2c0547cd79e946e45fcc651731c48980fd7ee3e184b54be3a1bfcdd8424bee71cc61ad2cd5935cd11c103a95422192b263eb0f575c418faac2e5c756b8f7fe0e45ead8d22737c14458094b0f43c2127925f774a59cfd06b1332637fde0b24afabccfd409eaa86f29bdc4c1127ef6b905be150d00360585e28b8c3d1c60c0b18fca6ff1730cff35c582e851704a11c5abaa94ee5776c6c905e6fea6f0d7ccd7ba5037baf6d68b05d1c2f6fd9adc972d86f03711c76f0a7f0ad26ebc067abc1ef0fc75bc64a79a1917f9c597e4cc2c46d686e59bf71029fcd613ba5afc1a34456f922a439d9f2666d78a212da366c7746cc97da6fc6acfb7df1a7a059d26c225b5437fa70f6e9f0a911d99ed0c44335efd5bab3c4d6d5c3383af4c1b63f581571748fd0cbc9c0fca1a94da3892907597db283631f82d295504540fc7e92d42fbf8ca8013f118dd2956d96429b504a0ab291cf5b06ca4025071a976d7c3cc69a6c53f2c180da99a3bc73f76818f499728fb66221a01d1a053fc30ee3866b37510c27702efb203e105ac20a489b727621489eebe04e15827ca51fac5900b9e603f31e198e4076620118d8cbdf4be7dde4f2291268a3c61749f2ac9ce2a8f9db749ff507cb77698d09e2b75889b7d2f81b5ac40bccfc33ad23eb18dbe4ef1b39712d747b6dd8753685462bed1af78deca9db7cc6710a571138a100d848e32471e1a11ba4e9a94e07bac0aae6367722d733dc4130ec14866757ec711c388ba51dc0775e80d7d0b2fac240a12c176d2f64bb22fcc1cf912439db6d4d25cbbdcf58f13187352e62015e88844fee42edb75a54b6bac667fdba1fb60d37cfc356c5b27187fec87052a9d9ba7c6a5ef9bc64d3f6b3ed2b8d47cdd03f4822b6b01082b2523ef7fdd0e601d121998d7963e4b28f0790b1864d8edd54086d251b21f2bce2e75d14925db0924ffc2ff3090968b8c926d909fb158267c46b80952d9668bef5d45e799be34b47d330be52dff25b8afda5b1a511a6c675a4aee30e17b5cb0bea7ae5bdc073fe8dc739dde7bfe0b05dc94152dbbea94c6e25e310c39cf99cbd658b34f7524c55a4ec36e15d4e2347718ddfc8f7b958f1c20ac38881d2abb1294c378fa7669f1ade6150265845c3120192788a1ef6bc7b88d6a923c14623f2889e2042bcefe5e9fe992938233db0d0e94724239611072d54e689ca7c29d01c4fe2be7489668bd2cb8e153948a2328af704ca5f2b8c225b431d585e2afeff25985e57bfebdb2fc937702ef534b0609062062ca782bbfdda99b95b6331e30451bad81deccd3306a5057d939317996cbd0140cf3f1f3518287c4c008718eea43b2a1095e7433edf96584d1cea0af50a1a393ef61a34b59a8cbf33e04c23e560ef7f1e4044ca353ab28210f2ec7f6b2d283e5aeb355f44de2f49a96620ddd573bc5792c29ca9ba4d6e80d207163182ab89f7f346123069465706948802b4089676a5fd6e9fe3d593fd89c9abddca5625e9a4e1cebadd548e3bd438850641801d95a46efe44f1aef05fde299396b36890bda71191b566028f81d1d692e52638f7f71f44e96e704d9ba436b5322f7b730582274dac8b9d9d126b48f24aec14136b0f68f997153ac62bdc5da3fd578cc1a6f488508d35c77a68eb3a35fc08bd2c31ac01a1fb755a446da9c0360a1326f5c6e35370324be80008197836451a2ff6395902f70fcd06cc762050d40f9bad2dc7bce97dd9c58558a7d53c2420bbfa9c1bbd040d0f254247fb2f22be98fa40cf945f2c989fd53c61f4e39902f3f7d55e84158b70e3796774349f1e54db0865d1243645aa0e2360217c2fb45cd2fc9ebb338123e7415b1795adee99b5a1b62f357d97f4157d6bcc04ddc417cfbf8f5d2a0667d2f673d7c4d4e77a10c80f421a4dca86617b18a1093923b26974cb8c9621a4c51bb61bb6e5587aabab799dff6973213e30186c5a915a3112d3408f1210e5248c38b88bf5d8985bb087211b0efb1ac435258ca0b6e4d4d9c595dc8ca6af5e30ad2eef9641fa5f6dd10fe2d87df5b6c25983a6e177c5ca60569400d9d019e2d406a698a9634c96faa35e18263cbca57fde515f6f0a008e1ff43d66c9609db2c4cd7d5f90a9dabc379cfce3f170f762a97accd51a8d95295c6691476be6fc3164563884531f94af6f823544fb03d909939412cd68dd58ab950004f6e291052f2897f317a25ec091cb41e1a982d24795bcc41dd9b22796fff2d98efe37f78346fdb8502b71e44f906f6e1703fd587caed53ff945e99e1b1b30f77ba47f5f5c583f50e88a02162c07d5d96a63653f5b6b48c73776a0112f1645a723f21b4bba759ff07e0aa0cced171f2ca3e29bbd30f76bfa41cc18e073d998a3a81942a5ae38bbb38465420e6ebff920e41b85fe1f8ff1ee894086080829f98ec26f24c3f1d119f9fa0c09570c8761624e1fef528df96e6b0945d26449659923f202eac85f6637e78e23ecaab5b14ca4f47bb9d3f53e76fc3bd2b96abb0e69ab2ee2c9e891b785eb99c2fe66beba15398d2a110169818672cf8f3257b80befab476ae8af3caf07f19a0f243070b7aa7c8c1bf7b441ca6404346300f65168cce79101d4b9f8196bd1db0e1cb7230b3d2f3d6840515e0998b48035b3d3210354eb0482da03392ed4a0154447e58b42e61206587d4c42b44341a69d0b2193d41d41c697772b278c8f3caa48cc18e361f927707a3a529a4804df5d3f545b3e659dfb14df04e70a2a323955383292b370476a4bbe27b4cd757b3247dad56bda009e3db9b8087347919b4edb89765b1882cb971bb2b2a64f741225d83c03de90b27f6e4fde6ce0a5bc19876a440065b985b70373fc87419957680aa980b35a8961a98a474f9bedf46c576987e0f6e577540642e796d057dea04addbad24290dc6e28f508f398bad482f6efa74b0b6f7d7f3fe90a85c31efb590e09a228084b5317c179d10411409e4d8a9c3972a2ab89195cda0e04442c1d40663dcb13ad0ed6bff2602bf3d89f2936d66bdf0dfd7e404d6ee81b6b03f3ec79cfadf2843347ea36e9cdaafdae9d1d33d3abb7703b2baa89ec21e82f292e9c0825b81d4372f8dfb10fbdadd1a6fd8cf932d08447d18af564312e52c10ef76697f4b61e8451edb8f93235b2333b0f17df9b30ddadae48fa55dcf5e22824b3c423f384d585d3274db1651194a755edaf0e53e181e6426b6c0bf811a6fc981b61d77252ce330b6453d91a09b4330bae7cd791441df6a6eef5f01412fd349fa044bffc64371bbc99f1310323ec4fe880c6b2c912780c2cc99584dc7dab48d6a8e3a696d161d76d2540c9ad7af9d7d248aea91f2b54315aba721b4ed14e9449311cbbe94c0ddb628269bb8ff15d1fb654b68b10a4247da6df3efa02bab43f0e0a2a8ccaaaa6a6a9455af0924171b090245ddd5364678fcd4cda8bcb9d08cda3c71c15fc3be4ed0a05d04f1d078362aeb301d36ebc0d60c8eb4f391c16cd7b709ecb6c5fe8bd7438917d338f472c4dfc15939e423a7242bb1a938c5852194cdf6d9ef917b15fac3bdc8cb4c44c33a8e559895c8c772108b04106890ac76b3ac5b4281a9166e4ec2ef35aa3c13c73477cad53c0cb3858a63afdd2e2c0b533866d48025c10c0094880e5122022cbebf4d4ea4fe2896d3b7bab41f5499aebdd255cfb56b08a342515822b543d34255b24cbb86b8b692e71b98869d1764cd1535c55f7ec45aae8609aabb5db8c3473709b3476a31bc996c4f749068b9edc892e214c3e24e9942beaea76efd33fdc8c32b181fa751b4a2ff0bcaee08b46d5461d1e697bec33e31f6cc79e4cc74972afdb393023bf2b6d7cd795c76c9927ff4b3a32824854c9d1c71be9357d9a7c1219a308fbb165f706d843f42450d36fbd598ecbf08a2f6358d0f098d7862d5e6e864f88e2ca68a26dffdaeed3cae33b39ce40a7433b5b77e0febfde4c637cc771d0055dfa65cded022c91bf7ae79b13ab4f9ab26f41014f56529b61594620e42b1507fdb23f6e7c16d4b140bdd2a1102eeb8041ca1146995f6876f58447d776589112f70ee85f5511e4f401eb6f3a05733b2e61f65a2abdd4cab99081791464735aaa330884070f97e3727148fd27e4e5f7fccfe01d08bc314c2d5b7a859a02bee46f3a175e5905f4fcf3f556586444509eb49315d41664e1f19797e4c75579f506649ecccff55735bfe2e36abf7042084a7b14bfb15d3d270788d2b8608f1220a685c1b53cd2f1685b58720f0611b6d4844ae87514e32d7cd3bb3cbbf523696c33ff15808c8d615e57d0cf7128ae590e50c2a508dd00fbf547ec7ca805243db92b2c9aeec46f883e79deae94a4dd21fd8a6d73468acd9cc3f34f8f9e7a0d91719c23a7f5eaa7e7dd481293a222a79fd2e4a0f46905cb0c7bb93f226a5894b7b275875a715b5a0ecd7c3d490635f377fe45a3a35564d5c4f9022c87de945084ea4fd3e59065afe1732612884e88dd2ed84ab1e86719c1131579b1108acea49f8e9b3bc4ec848e694402f7ba5ebc99ead07a81c0c2a2605ff1df4fc378483df91e49ab7a8efb0aaae709fc188284dce5e9998aa2e0e27fa3c746fa6e2e8c91a970afac584c14a334aead5fd43a4be968ac5671af27330e20d0f2925218c2e6c335ebd3759c1a4815012dbdd0c8d48de6fc52c64216f7363e8391785fc815dfc92aec2ce7d591566f171e6ecf48670adc7eb0d68e58a7624951e062b60b89e6e17161a90dbeefa99345cd3a0ae72cb4aba87f697c137f45252429d5692a4c2b2a83028d9688fc357fab0514bcb8aad120f436112ece6d214a87d2e35367feab7058834f6d45f2156e2aefc2debcaa35f38e0fa0fe6eb2670bddbe472923719a4e9a1431af22664b26521332ad501a34e1750bafd7b1762686001c4bee3570ae692ad1d008160dfb37a5f4084978930815e1fc711d16c808bbb69d9090bec2ba8f2c30403989c8bd17486e96cc88ac0990663fc0277debf7cd1ce34de336977f0a13e777651115fcac1e1278338b6563625696f956938d2fcc1f12c4bf06c85c2cd7e5ee441b92ed8a042f11c713985c38f7e4514d12e3db65e3dd7b83371f01b7bd0f6d5ed20893e6f16b957bc931818af0ba55bba35550e7620de325005d68207004f00ca9568953c6a8746cab926e7ece903dbaa9de940cc54830338f203b461aa8ecb38d35b4d7726ea99580b51c1b72e233e59fea2517ac5d6161cd720954d90c5b6d567b7af060a7db66d61384684545e7e85fb9c0944c9d8a10db5c036a13c954a4bf7546b95b39e0233ccafa1ea1c7c0c9b19dd6ac762d6060fd5c22e4fb6a110a0743cf84bc004317e2131f2a30657fcb055c836ece838e71961f7f79a3c5fcd44db5c3bd2392e796531d08dbbee86dd60d382e50ad815fd8eb8f5420e9841645c7f73972c1b7a47ab685f9308db5e9d85845f19efed48ac813169faa911e8da9cc04ec656e668b72e228b4c9ffb9b86f3f080b33aa01e3a1baf696877a15cc54fd79eb2cd67fdeb9fff0c324cc99ecd2d7152a6ac1c4f6aa0b3e9ebf98b0d7c3fdf212f510a37779f51002b9b8875647caf3844d27972d9fdb2b79d61e677647d06b084ad62a22aa33d81c4d8d734fcbfbeade3d3f8a039faa2a2c9957e835ad55b22e75bf57bb556ac8");
+            Assert.True(Arrays.AreEqual(expected, attachedSig));
+
+            signer.Init(false, pubParams);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+        }
+        
+        [Test]
+        public void TestBasicKeyGenerationShake256128fSimple()
+        {
+            SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+
+            
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(Hex.Decode(
+                "7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4766BA69D8560A9F84846AD8B765390C84"))};
+            
+            FixedSecureRandom random = new FixedSecureRandom(source);
+                
+            kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, SPHINCSPlusParameters.shake256_128f_simple));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+            SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(pubParams.GetParameters().GetEncoded(), Hex.Decode("B505D7CFAD1B497499323C8686325E4766BA69D8560A9F84846AD8B765390C84")), pubParams.GetEncoded()));
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(privParams.GetParameters().GetEncoded(), Hex.Decode("7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4766BA69D8560A9F84846AD8B765390C84")), privParams.GetEncoded()));
+        }
+        
+        [Test]
+        public void TestBasicKeyGenerationShake256128fSimpleSign()
+        {
+            SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(Hex.Decode(
+                "7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E4766BA69D8560A9F84846AD8B765390C84"))};
+            
+            
+                FixedSecureRandom random = new FixedSecureRandom(source);
+
+            kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, SPHINCSPlusParameters.shake256_128f_simple));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+            SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+            byte[] msg = Hex.Decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+            SPHINCSPlusSigner signer = new SPHINCSPlusSigner();
+            
+            
+            FixedSecureRandom.Source[] source1 = {new FixedSecureRandom.Source(Hex.Decode("33b3c07507e4201748494d832b6ee2a6"))};
+            FixedSecureRandom fr = new FixedSecureRandom(source1);
+            
+            signer.Init(true, new ParametersWithRandom(privParams, fr));
+
+            byte[] sig = signer.GenerateSignature(msg);
+            byte[] attachedSig = Arrays.Concatenate(sig, msg);
+
+            byte[] expected = Hex.Decode("07EB19E7D838D71EF66B8263B5D1F8EC7771DA070339C029AC6B0950D25BF204F07AAD69E6B604317BDEBABBC13F15A61ADAE791EB952F8BFF219A8FC3C299DF441D8E279FDCBF59A13A2777D9477B7D1B3F6408E5EC808E080E8A341A9C3889C745EA1889DED1B81978BC0EABAEDC0627920A7D761698A8FE8B5006DD2DF270EDD0875A4AD690D15CF2C5561DBCA821C3F8DAAD5DFA0D6EE2FDD3A12FB0898A7B3437F7C9D4B256A276BACCE8F8D289BA8FC7BF9ADDC089E416501A09CD7DE090778D0B160ACC84E8FD710203C95812964F3ED885A150FE5C069E66A8E22EE9096F8BD546DAF6EF6516C794D37D6865B948A0A57E23A3075AFCEFB687FD7114A96DBB950F38884DB42D176353C901EDAF17F01F9C17B628A92B116962973F6BE88958A7F93AD91B0A1F25CD934C676DC916E5D66058FAF9C2B9F99835AA65D235D679D9CAF0DA7304A8E49CE09F4247A264E5491913987A4C65490837B741B2F459C535D692DBBCD13D8537EA5D89DA433A3D1598E5EDF6394E31CAB082ADD12FFFD701BCB355168AC9704E5B807FC036D5F9D323D5B87D65474C846ACB637DE7A60CC860F2285E2CE7F9A3CB067FB84AFC408DDB00996DC8733ED38DAE59A93928949A41ADD53D31E336CF8B253573574E99935B3299A3111B311D1C249EDEADE212B8936674E7A24217C3D78512A24FE5336D472D0FA0E17D928E899DAA25F4E09AB3110F7113F127AA0A97B0A3B86817ACF71CC696DEAB246FDDC7158E9579E9B568648684DB6356B4692239ED7503A0A7FA6F35775A29BB43BBBC187507251FC74C5CDFD59F8E07EAE1A73C6D1D12633C73010FA1AC7FAFE531F59825BB9E884CD8EEF47AC850491156D2A5212C0E9E95D59F132938F2BA00ED9E98E5FA2941420815B0E0E52AEDDBEC250E6B6F2EAE9B97F65E320F96C6BC6073404467D16D0C8782CA2CCC5601C8433CC680D69C59AAEF136F19FE48F9D8CDBB0B28B119F5DF2319E817540FD0C1AD0521C5E7215D8982BC7EFE7B23C01DCA3347200057959EFCF02AA221423632D9DD92AE9D435C2FCB2C72C0965F859ED89A0912F98EF987BC866AC7E681B28F63E7C1DA9697A55F340326512E857EB2E2CD39BBEABFC44AA53FF648B989B86DF62B5A23332DC7EBA2EDDC874A7A4E91CC4BA90D92048692F27D71238D479688686BCF6152CDBAD06D95A1A5A170544C25F2BD72EB1DAD381E80CEFD5453F41FE0C66FAFD220AE7505DE5F15624E5E02429444944D3F4483AD9DB2D886F3D6078A18C3EE99637F0688D52AE682AA08C49D637CE9AF9642A06D30E1ECEFB8760213CBCF65A76A5592CF4CD3B962990F174CDE68CF9ECD0D5CC5BCD832DE1C543E521CFDD2E15405033026C7A01FCEC0FAF01EB8DBAD4BD771EA65F57CE0FD1A7A5DDD1B233F2E1881DEBF6984141A7F2AA5D2237D187B87FDE8F622A2FC1BDC1759307532DA46133E0E02D66B11ACF82A8D6C0E071FAA9EAF2C9A469D4D7820EA43CDFDA50C81EB0343605399867B174EBE758C05D331479503A14799406CD4E24BB0D4A2A8064EF75C918B7B36D1D66EC02A499D960827A0D21B78FA0A6F06B3E20F45FC848706EE9498B8C3482A90F7B51302369A3C4C012832317729436E619DC1FD1B5114C63F42E1C7D7BC0C930F27C4CD5A4C15CF3AA3B32C7FDB3FB949E4D578CAEBDC741B7323323A3872EA5F8A6302968BDF32326C9721D84FE2033125D066E97EDA7601910C7995A866C08D7105A2A2D07BA90178BEFF433C14096C1A7712ADD3B8A1F9D85BC9667EE4307BE4CE682B3C1DE50F69EE6C165C28185CC88B997D2F8F259A3D7DD9C388DA37CF342F595A285FC02FC3454B8EE78BDDF02B0DCB4574649CF35311ECE53B9A4F3FF10182F5E0F87E4D8B97031E6A53BB5F3E24916F4665A0F32197302E5F6674E07151E16A71AE7C27F4DFAC78300E323C47EAB2943B93C41669FE4873EE7AB5B097196E0323E69FB078F48AF1717A8985B1E8B518BBCFBCDD1F85ECBD8C5054C0A746257D9DE7A43094E48814CC24E2C7C4ADBFCCDB5F9E705B4C42BFE687C475B476B4DC66921140445565B74EE861402F18178435BCE3B698781E5D00E2BBCCC5AE50962F4F614B7ED743991198D3B95B7E2DFA12869A46E51D1D617B300AC2275A88AE118AF4C78A2A896CEB9781E1C6DBD604A1C0D5734472233246D5DFB4F24BD732459E704E2CBEFBAE494B8CD6E53F922430617D884F79BB2062BB1553844025CFF41482A8528148CA7FEA121F3709B95EBB1AB146BF14DA17DFD490C954484B12D40E6F0303A7AC269A00A41252044E69ABE090735E1212ABCD8B37232E1073333CF48127457DBFDE297E80721CF813EAAEB8AB1388A73B506AB712D40751BD9BB72C3CA22BAEF28DCB4707B3852642DFDE11D911DECCDDAA9E8AF1280E0CC18C30145FA698F7D7BEBBB728C78AF46FFC37F636A4E84ECA0713D1D49EA60FBAA6AA6DADFF2E1A207863E3B23927AA8C877810B42CF0E5B2238AA6DEC299ABC2CB817DDA371F5168ED4F66AACC7F14A83D0A7CDE169065D1A321F5B6AEC756B4AEC614D21E35727DB005982DC4DB3D5A4790F25790C11ECEC64F60F26E482CF03534C6F78757C8728A0B3C10FD9AF696C3684A6F5918207A1D11FAB7372C5A14BEB5961B2BA9139FAAA4854316942A454815AE7271FCF13D8D9C980645CAA5017123BD39644DDC23A58AF70A98B8832A42F84A334012536677475D94DC14A8FC6BAC250C2A1F57A84465BF34DF447B3C9A329D8459B64F65434BF68887079B0B70CAAA917C5E89C11144D6B277A28BE49B5C5A51F0A7306281421DE9084B9B6570EE7059558ABDDB23B824530738E8212D2FFB02A35CAF604AED36D6FC014DB21595A8058099284DBA99E69A82A7D6C989F9F7FD00125B962A4979688D118CB0E4E520E71B27DF642F17FEA5297DFA56DA3D39A207E147D5B1203358A0E88B1997CD9B6DB50BD60A0030F5665B138510336279B82F1F87525841CC2FBEAA7EE9E393D6BCA877818C628C9E1C0695B24668E922E82622C152A801C0F8C5EFE2487ECEDD19D87F962BE61B2F0F42B4A9711288FC86AF974B0384F31D4C6BEB44F3D091FE1EE26A893C49837C2A7792F1EA922FCC6B4BAFEBBE0D055FBF05159BF43B34868FDDD369F7396ECD0FBFCB767A63A27AE91FA3FF4E15DDC0B17D748DD05841B8308109378785641DDF69D9230947B7634C682FE36484C6B601E2ADF5CF0CE13B8010E2635141109AE38A4FD8BCF6052967FD8575CCDC23CA8143A6DCDDCCD026EFFBB2BA515495521EEEEB2B912A929DB3F1AB730F88D3152A85D02BB1833E1AE835D7CCDA728155005AF73AC68AC402C294E319B3C9DB4045A938C546248D1B1CE0AAAC836D1FFFAEBF759C85AAEE3D52BE95FD44290C93B6DDCAEC2C488C2C7E2FDC74784E1E28FEDCE41472A86D2DDE75DF39C57B33E83BAD90FBF9D6C5DA806409840D33A58609273D026858963DAF7922090838027C45E37BF6E7B6EFE16C7716ED22AF1A89B20AF26AD579146A73E9AC3720A9FFA08F7E7AC5B5CC84554982A18E7672C0A7CEB9A4E1C03A61A1B6573C119871DE7E4BFAD32A6268E15C87EBAD2287A43D71FE33A98C0E9BAE2AA6FCBDE112AA16A26B6655D8FF4C1840D9EC9895E5A45379A505F0FF1D4B5D7484FE9022080AC4CB09DA9F2757F82517B2EA753EF6FB20FD8E420177D2036E3E7AE0CD70B2156747A60FE71FD97A8E012CA50DBF3839F41BC96BE8963BA525D493512E9D4FE75E205B875CC1B4A2D8C2BEBD183F4EC3F160AC51C7AFB445A8CFEF63A74F0A20EB6442A70524E1B166F4AE9D9A41E1E81853945F7FE7911B7B59610C35E291CDED902A5215BC9B5DD0CD4E53D130DDA39CF3C64A062FD45BFE603951320A490F682EF2C6C21ADACF29370529906EAC7B2446EAE290D7B4357BE2889610296BAED0F57D69806C2F6F61E6C6765EDAFDF798D2BC9975D01A06D90E9A1FB3BE87F21E4CCE70138FD9D19C86DC9BE0C3DE531D4F32E1814273E6A8C9F04A4CA45FF533A5CC48DC00ADC0EDEC6A32BFF19066A79966E273B5C4C912047363879C79C26C6D2039365DB07366CCA8422DE6C11761061347D791062099CCB10093627E48849AB5C576C58C84DDB21ACBB2101DCF5E0CB7B0431C59EAA753F444F8FF85D22D550276C8B8DAE52DF994FE8F8D3A8A2C06CB650554552D4DA921CAAFB99F442ADE1E34F60AC9252EECCEEDD0F2EB4CE68A7A9591F4B7EC881F1CAC4AA7A18BD04E144FCB9D1353C3496F6556F7EEEEE56FA20D7BFD8A441D2E205FAF637FFB456F7F476614C3B7F32395F3E56F64D4CEBC96878586D225C3573CA972B3DD5488E51961305C59F6E4B56DD9141AF0B36F405F2079059E69AF0AFC9BF3212181ADFCC8F346D0B1440B8BEFE12DBDB314DF6A18260819AC877F0AFDB7C2E10E0235F8A4DF373C17E778C8FC7B99854F5BE21C5CABB514E66F5E159F4DBBBDFCBB3A9B1C8D0327A04B10E9FB24FB28302DEE88F74F06BC0DA97F51ED581F351E619018239D23B09CADDBF7D0D0495D5355A37893EE08C49E3A1BF2BB5FA1FAD1C4732B232E50F33E8956DEB2D83000051507F4C5BDA6D5539439AE2E67ECB3482D410A721A3F30806AA578079E0AEF839A7457916DC47600F5AAD3653C3F8C004F852461CB462C470343F5E3D9230548D7EA309913C639B3D586FC418EE91AABEEAF689703B062DF44C88013687F93C6EEC40A66864012EDFB55FACF0EB607E6F1EC876B9A24817C29C7ABFDA1E9D1171EB61403066098730A35FCA0BE80C8C26536CE5F47C8294E6EA6C43A6DC721BB78987AE90182415D17926C75BB8A85A2B33F2AF5463A87E32EDC1211DF7E2665ED258B0721880C8A0EF0C926173BCEA216FF1D3DA8E757C24F12ABFF6E7B5BDA36D7FCEFCF2011D27A5E6FE450999E4073089A0334B23F85EB02EEC0225454F341F026194D59A8BD1889F1FB80B9555332DA2323D9F0FE19268B51CCE51D641CB445271039CF1E5F225589EFA712A77DE73A80DFDFF7EFE34613E3029112962C7EBBA5BA9D847969914B4F7C872D79DD2123F39EFA6CEB02B9189B5F45D717268218FE145DEF10DA29AEBEFD4EEDBA8AC73E0E26BA97BEE2CD3FDB6056F4747FB80A376FE7178C8AD5A8F705F11D1874336C4D0F55462176D4A3A2A1E2E3894270845687293C75EC48361ADF5DAC31F46A524E2379A18C29E1B925AF837A4CE60E0992C06E502706AD88863B4D32433D8539559C054ECC242E556DEB62EFE044AC83B9BC00CED0F89F0BBB5D997EA4ACCA2B5329257A8D52860530A347213CCA7342A02EF096C938DCD6A7D422FA6A839EBC43EEC21FDDCFC03BEE582CD81BB156A578C1658CFCB86D08A7718C182D3FD8CBB54C45846541CC519D32E977E7AF1852ACD7FD402400F711816FAFD2883EC6059653CE93E046FDD6827DA1C344AB01FC5E9A16FB7629CDCBD591A8578364BF2BEA721A96ED17E015506E9F070984C3D392583E5A4AABE22723077AABEDAD783727E3128A730B0F9F73C82291A20F245D87D61A3B30404DE15EDE426DB6410FAEF94B5D19FADF9465CCB1431125D5D71174B3A43CEF650853E27708070F47A9970976B49C79DC6B4605C7D5D0A0C11AF5BDD47040095CFB034D1B4539488AE18837B54C0285423D4B2F5870D29E28AD58CF62B8B8FD3CD16FD39309318AF00268668D9BA08D122CB4C644B9120B29728B794720060D14D014B097F0E66E53ED61889E7CCEB4C6E2963A01956D6D4AE60AECAE122DC2B021A9C59DBDCB3621F5CA3A69C622A341E0BC80C2952FACC152EB2C1ABB36A49A22D57622ADA1E75695B4A29276645E225625B23130CDEAE7153F7B7A449A078A6CC4E1410957A9F193C84F6F601B0CE8EDF023A81F1CD78E322214044FA03515AD71A490A2F406ABEED1046C16E149CA689E339F9626196042B6AE7740CC6D89301A3B7ED9F91FFA04A241EC0FD594D1C96F8ADB7E7B333DE66EB23EFE63ADE7D51B11FECC9650ABD29ACA08B033372B461390A4EEEB5BA1E6264065067B0C19F5107A51A656108CB9306141D097E6711680985D0579D41BCB84A8EF469DBC54D73B47043C8202BD20BDD9BB02679E1522691354F8670A8D371BBD366048B68E43C9CAEFE219A8C6422B461A0FBBB2CAEEA7B8F215D4BAF40CF213E6D3DBEFF8AAA525DF66CCEF7175CC2AB42F75D0A40FBEFC2B08213B6604B76B83B6984D476B853C5B32922B49635E2FE1B28D79B8CF91A222BF9D614F175B0ABB8D2DDB6F088D083E556127D902AB458E6001D798243FEA2D38705E2E92320F3483EA6528D907370CFF12610A12AD196F88393E282AD53BD254C4B861D342B2791BD07320D53E05D06A1FAAFAD6E252354682EE824BF23C88188FCCCA4BC4CAC44832B402DE9E23050D8BD1A5E606AA65384B2FDF58AE73EC1181382C68B6C1E5925D6C63A8D2A7D8D4C8579B7C0493B308EBA488869FBE1CAA0F7FB826CB11B81AD31A70DF9985C4C5A1C6C678C254A918CF95DC4B1C0C460720BC2948CCD125924FADAC31C86C6B69969D8726AFCD521B3353935562FF4AF904D7DEA50BFFA02E1B6A644A5EA3DD393F52A5322F40C8ABC156340DE79E261BC35AE398FA40BBA15136D5CD5F5BFFDAEF36456FE348B40438B7054F054F08BF404AF45CCFCEF735849114187279606B48F0878B33E1245203EC89E04E076D60D196B27AD0915160931467FDBE1F3448360695C459D13158A868E4A3AEDBCD4DD35989DE1FFFA0750C739FABBFF11917055286D75DC0447DF497FCEA44253BD1A0D7A7B02405DC16A5FDEEF3761A84C0188F273643AE02F8416503FC1A420878E9E8416DDECE023FBD7E8EBFC703734E10465EADE9E99CBB0D2180BD471839A2330A98A8A1BCB5DD5064567D367B06FB62BF14493042EAAC9C0A93291F51D7A927768C795F858E483DB7137D3728F95D838D92B8EC94E027A4EAAB535C15F21EE78B7BAF06AE03D0CB379CE6E88328DDE5A9A70FE2D30A944929585BE02BB5AB45C3A88D3B82A7E3BD4E061C92E2F97DE0FFEA2F4CF6BE3B009DBFC00FFBEF0A9032675336A1FAD11C367978969D88A76B2CB4CB95B09EF2D3B7ABE2B89AA3DD7379905AD8F471A583D000BAB4BA35C6BB2AF44392D65FDE5DDF45F5A3EEB971F16B4DEAA63B0A7F750420727B9EACE87456A2665510CE64738C8FBD0E9E37BCBEA18739A3DFC40F5A515F2CE7F0F77C876B94089670CB5CE5466A90D29BE12BF5EDB0A22B043D089A982862B82677E5EFAB7383803536E6809D2BEC8A4680A0AE747D745B2680FA44C4909F9768040AB34731D6ED94F10117F015A3EFB6E13C6AB7406E0A77083FC89EFAF1BA14D381B75B98BF96B85E5E83BBF98B8CF35ABE23FB864468964F74509CD88FB2F6BE65EFA4E2896FB8FB5B74A409D692B745ACBA01B292A2DDD8019C64C86FC7390F744A21B4C0E921F893157771A3047A9B0CA80289C462769619A53996AC451DC3DE6B255DF1247AF18000EE1D6FDE911EF193029EF325F1614354D0378816AE0633225C7B6467819E2F26778C65AAFBFD8C41E031245631F31664301D30A44C1674E7906AD458667EC97A9E101ECB91A418D077B980237BA9322C11468D66E11C68721CB99A28AB849BFC2207220814177B68BC72AC544AE4786B7302087BA621DFF50F685F77375A9B3ED7300AC2A6E210217F87A2AE79A8830DC604D63F3562D4ADCE418A60CA8C2BA2B5BB4C96F181EF1656209A4F3F94B63016165936C7B0411F21B9504B35AE2CE6D11BC8B9C51D1D4726109F461DE35C31FC2C3A6201D5A7DDB4ED1364ECAF6BADB3203EFB576C4E9192FB50CE1B458A03A872EB0ACBCAA3235C038F4CEF6CBAD28E3E03FFE6FD1A0DA32B561F7250D920AD0A542FC6C729E9F060A4C52D0DED565415BF73414FD9D9E6C70E4C045BB8C08F2CA0EB2CAF824DEF86B7811BA1F608A8B198057498066547ACAE05F7967BF71C1B51F99B61DBBF8A1E83BD8F5E41678E7BC94FBB0284CCCA3A8343137EB6A09D1C7043A9B9AE9670323E50A54400CC22F8427748E1630993C8FD9CD6418761435E0C72EAEB5F40B9CD41C4F62A06600E321E7132217891521ADD882A5DB4A7B2AB864431BBD9BB33D89F5303F351250C309BBB84AB24FBE0475AC508AC318EC3EE226C95E7FD5A9D59BB27841388FF166C9C4EA9F77A0422FBBEE28FB6F3DBBFE25C400FC385BE389AD6F63C9215107685427CA60148CC1D301C3E7EA3F01AD68BEEF8E11F1BAC0EE3161E6A5912B5B2B898D0DD1FF5B9EDF7031CEDBD512C0C871D11C770C313CA02713094653A4DE3EEC4D048AF2999796FE4D13879A1D56DE46423D3D69CECBAF32934F45CA2F0367DC37B4545E15D34FA7FAEA8E8FF4B329A52D974F9229A331E4AB05D861CA7D778349BE6A8D2A41C21EC343890B146F3182AE185FDEE5159F4095F187FD69CDC55BB5FEC8FDF36EDFDF39C7E96E42AB692996A2CAEB520E754CE7E46F25A6D0ABBAF2AEC2CC84FCE16BF781FAFB12395FDCC334F4C13A3E68EF3E5CFB6266468CC15DC207785CAB12A32B677F4EED9B362FCEC608A9A6D314852E9C8D64F234288142F0EB1081FF59A71938F6E614312AC57F00255AB75B05C88288A275301BCEFEB5BA17AE4DCB2DEDEC7A68C7D38EE1446B1A1BB8BEA27E679A204A8BB494C9D041C9A271BA0937C5BAE276FA6ADAAA156D4B3015915331DBBFBCA378BDCCCF3519DA1BB17EA729796A19A4FF2BD691F44D2978A0E977A893FD71F098F3531300843EFF8865808EFA285ABC629085F9D3A5D91A24A7645E5CE022DC20231B5AE7C6617FDE1646BFF26FCA5E1DB6664F97FEA1941496141B1BE6A0D679D10F2D2B6A703B22DFCE5A65B5D5170974F4ADF0398424F61F6D1ADF7AE6587AF0D1FA6EB79FDBFB4B5E81D858B020A5B1A750C5A797E2CCC1BD59C7A27672DF43DBE1B83855EEC0CEF7A5ABE48E10B4E320D9FE06BF0788EE71678E2B8191D73A6F272FA0DD88B7877B5C82B73080E9718F0B5ED1ED14EB6DB0811D7D14B96F994027C06AA613A7905118C5D9E0753AFF289758B2707008694049B7A9DEC420B0AFB53B7DC88802781878D18062DF43847FE1C1BBA99E8A8DC901DB2F10B00F15F6CF53E3B0CDA27B170D7CF14B0F26CD6D3E389EEB178DD1A55C638E4CB377C4D6C26C7702BE04668F0873748B8E7BD5961C60B8741D9CD82910C844FE53F7E620E06EDD00F35ACCA2940759E89EBD5E63DE89AEF0FF54BF5847E3911B5C1459608EECE53678D8D52CDF2A47E980ABD7D18634F82BC880A99E1551A41474A96761095292FC36C54DA23CE717650BE530587312EC4BFF4D2BE61432E950F9F23161CA8CBE5CA047AF8D7B1367D37EE5123AC6830B9D22F8CF845355D770DB1C1490BF621A4F688190A61EDA1D7762B3AC04467D067AD83F07874CB35B54AF2A2DFFCA5F7838A29EE298DD1AE36EC29584390F51BAB6E9484A3CCD7149633331159462CAF8DE431ABEB0C75F8348D201ABFFAF63C8F758820377B4E486AFFF289AA29E886F067BE16B1611ABF1C32449B46628AB8EB96E647C17FFF5899111130F38B973F7B571FFA154D4B0BC9AD11543817046F428101D1CF6F3C84D5B002115FF7F881CE3DF0AE2280DA5B9520C653A4A2B95694F2547EF5BF9DC3CDCFF2E4F30E105034D8547C92A1211CB91D99FD49CA9C9E332B6D41DA7592AC260B63DC46122E9B5D3B4CEC905A5B10B58B95A14B06C1A1E07D922DB43259E51FCF44E37CCA2BC5D75006EDE5DFF50E263047F3C4C488DCE27BFACFC5C74AB2D26C41430D5F140FE3B30EFE19832C73504124D75EBC1D01145C419AFD20725D86AC126ED1EAEEF67F3EBC403D20070411506A5AE58E6AA736B5A72AC23BED65189A2F0539C38130ED2E4F6E9D1ACAD22BB9180374CE427F9863538402C58A7A6054F8E6BF08630377F8253791778CD9BF8B32447164A1B82476A35D79DB993A91995F108E3630E682D2EB6985F9B63F7C6798C3D4B2A28BB98B20EC13108408B43021A1F57F142F89DC7D04D0A6F18083717FA9C64E94C890A138A7E956E706178942F0D22E3AA638CDC7D32E3D4538A29EB9C2356ECD70E9E3687A7860F57CC6C40BAD063D347A054D488EE9C313A128AEDDC98EA729A929C874216B4AC45ED0EA3D38FBC55DAF4A7E554DC06E70E1630D73A2C1FB58652A61C7E96B23155044EBB9F94901855F04321A635BBB0020394A342CD49F5FF8CF8E01BCCF671EED40AE66DBD74D5755258291E401CD6F15D4BB61730F971A602D996C538C5C00BDA372EE498B06590A4FC247D23082ED973DF044268DD7B223B448F5E635608BC9FE72DB049EBFA9106E910C95CF32AA353E1C3EE652319FFEB025F6E41EA8B7D5F4FFA80F85582C9C2041151042D6CBA6C03A4B64E5195C40B38A5625B2FB2214BA10B613C134CD7E6BBB24B9369420411E8D8D4E467BAE8559352D7A5E709884FA1E55E82B7F5E327C2F7BC585BC6F885BF603453A7293D93163E691FE4DC589CFEDF19C1E93EA56646CACFDD738BF1C69BC94D0BB2274298FECD8730D3BA98DAFCA422A3D7B7915947DCA4230EA342AE3DC3B95FD0680884E19D389608D2118891DCCFB1271108A22E6CE0C47A661E9FB79C68192C423B3EF796240F9DBEF9D340CF830E2D217C17119B76246AD863676B2FAC66C73B6ED29AC9A402A90BEF1D2DDCEC70BFCD6FFC7AABA99BE366256A03DB91DDADA86A332BDEE2E188B8D44D6A669076956B129BD4E0C24106FD381EDA82E8774F1F63B5F58AA151FE2BCE52B659EBB5CFFB8DDB0D8CE29E79365AA6051D667A1B52D3B425AB571CDA75AD6749A2105C2C218B892EB960028C02D33679CD019C7E7D67930EF2713E563E3C3329C5A13205CECE865534F5D7DF4C58D59B1E714582B89C9C5DDDE32822358FF4B15CDC6A9E1381374E8F71EEA99FF411F7B69F0C0B6A56EB9ADF07C93270E197EA86CEE316467E71434AFE53F30F8A2A767C5661CE54D5B3A453976B4EEF38C0B93E603BCED33444E4D409C26B7FB08E64C8F606BF4D52192A4318726C7DB987B54547DE95B5A7104FAA315F4B41AD521CE1B86B9E32EBB99F01F093DBA6648BD6554742A590A671E9588CDFC575AECCE5F83F5337A6BDF24D74951F5BC5831FDE9E0EF9976BEAAE63D2CFD55A72729DE83229C3C102AF5DB3AE78D63DD830FD2FAC389C3A6DE0A40F0F1FE25429758C7288CA71ED3F9631A3F62C9E0CFBFAB4644FE67CBD8DD0D5C3FFFA36BBE4CCA223488902DC9B1DA53E902FAC7B80F834F75D4DDF64BAB68A9A489DB0D6BFB346F7F698031FC163E2C9050FC0D8D975D5C1E18CEB1562E7125B482312A471E6E1AD6502FE6AD19E0152B79F72708B65571219D3B9718DF48D7ABD17C764EC7188B256B88EE57E3E178CB35DB66DD331281EBBD341C9E84279B82EEBD7EDA5651D3F208252CCF4F904FD7783F2C49DD5E54A30B70B2727EFF4FED95906E1529AE86B804A15212030AC078C18FA4F7B257E86C9B0B960EBEF4312E67E607A1CF9D890B8BCDF5ABEB102D8AC017CE79B3D73BBA6124F156F8389EFAB0845A3B064E5FE9169B4A59BD3B5AC2A2D406A88E5D1EEA709A26DB7BEECBF66F6CE779F2938A0856EEF431BBDE237249490A7977BAE1719EA2D716B8430FEB692EFCCC95D0A6FB395D11538F70FF14DA4A6B18C1BFBD3FEDF9F46F9980616DF6D32899662E4AFFA2D404773ABA2C5BCED9755BB86466493EA183F2F973A3C83B673F67DE435DBA74D855BDEAF08158A72B0961BA0EDFC3AFA7A37F6E1A0B2E41FAD5483FE09D53DA8F6552D4A355B1A901227836FBDF606FBCA22DAEFD69FCF6264134884EEAE3F25EDEB12A1A529D88A4A1CCA8BF91AA4848388DA6103543336CCD066FA5BCA802167EE90EEB31B96B09117C39EF54332CBC046824A071EE664AB1572F25756CB62E9BA47D354DB5E3D626BAE843EE358B5871CCD8DC28D56D9D6643BDD5B0A7B4D3EA940BEAB4BB4F74395A7EAD66F67194FCB5FE0C90F0B52C8FEF5F33A063E114417431B24B6B8B46CB75F21F9342FEA40E002B10E93786697D290F0D3C08BBD17AA375B95472297C82F915DBC95315D6EACABAE5EE1727A980EF4778A853546146BF873C861863E3AA51A6D31A7E5734ECD325E0B05603696E3DCF4B84A8C4D5FB50D6721D8095E8AB5C795274F1B7C4C378844C67A1C01C15EC3CE855243FF41A0B75BA75BBBE5A73197049F5BEB5D51F0FD937A079D9BEAABB0F19ABC58CC1FCAFD3FC60DC4ECD5B612AFC7296BC378D41D90E1F0BA1802D02D8825E9DC27BE997FBA04493F223C5382EFFCB5CC539591148089258DAB807D1EED1C0A38CF574C2BDC16A5E8D98B0BA9CFB7E7BCF008A791A5DECC2EBD4272BB16C92B1FCED121C25C29A9CB688BFC4CD5CF4F77AF8C1783DC5E6AAF1B40B45E91C94A3F3D31DD3663EA4634570E181C98C0333DBB615E7F3ACE6E1476EF2B0D7AA540D5B37A4E1B3327BAB4F77C3AA8EDD0B075F42069BD2A43C806703AC2D2961FB5E87FB522CC964C70FDB12CC2F3167E1371D941B71B1CFB484BEC1BE016AE42D11A9A2AE440C4B0739EE8EB9F7A7CEB07324AFAF142CB312481628BA098C4DEF89A29BCC6B7D7C796F2106A4B7D0E269813321E0A690BCBB51BBFC097E3DCB7CD915A0F18A5142617A470943A01D98BA5B79BC84E205E7D78389A1DA4A3C48C72A1C0669238A183D8871E79D847CB93AB2911436C7F9309420CBD0FE0EC3A1E86DBB734F79BE9674AEDCDBD1EEC761513C8AE4186F3055A0178A0F49A70055BEB41A6EB2E764EFBC990BB9B9EBF5A8A4359B0A11279A0844207751F090139A0A204AB307B1C3C6BDBC424F4DD5AF63796AC3DF821A2F6C11D8231C52664B7484B2ED5AFECCB99BA70AE421591A75E30BB450558FC7860D43EA38E689F9FB7AF3C4B664E8E4174DBF217B913D82EB088324ED1067B93E23971CDC8E1406E7DB9099E41A969BDC035F67114C9505C65868D4186901E517AD38E789533FDB52BB2848CCB766817A27AE7724FA470B1AC3D2AC4115D347736E3C027DB4B20F7881BBF9C528BAB9D00FAADED44CC5BC2BF4855270FC5F7BFE71FF29A49EB6C18294565520B20291067C26CD2F31BFF82AAB40D8EA045A9AF5C8F7EDC9BD85307E523200998FA12A5EBBE611F64F9CA0A138DEE3316A8B7B99DBDA800C0E1BE26D6CCF3E9B245E38D779C4EA8DD51A4CC6383C5F5CA506297622FC27B6C0137A3B4D8A595502082022BCAF3AFF45E6514248F4958783DC969A31DAA62FA85595328BD826C3D6F4E623274EEBED9E2B97935BFE04496678413D6727A424F8D6227C89CF831CF48B0D349214891841FFC3F6AC359A2BEBCA668BEFB0A6511747CD9AA00EEB894BF4856CA3699982DA0B415FEDDEDE4C143071C27F1CC61BDBCCCBA9790FF8E0B63622A2466020BA8885F345B73115D7AD94F77BC2ADEC710DDF387581DC6E958D155F2AEC56E7CF6845F64ADBDED4D591E8E848455B27D88C868B9ADF049EC2E86F3E1BA9EA006CFA8CE85EC1952E55D823641EEF3264AB04EF9E3B07460C8FA7C2D2DE24295540DBC5A3C666B3A425DD5BC82A0BB8A7EBB890EE9E45E353F27FACB0C2DCAE0480084557573F802B1554A7D847735A70954A1DF5FA98E368595093755352DE5876BBE88D5EBA05FF49C0CE012CC3F399DA28B4F1FC5D378DDAF72919BF0DF0E83B410AD14311461623BB42C9C9D07873BA27205485B0B7A0637853339321848A96A00FE9D0A220EDDFE0CCDA9EE952BE79C28CB97A893044777132799E813A0E79F3E3BE680350EFB35DBEFC96734E17D9BA4C877EA938F098B3CA370D74623AC72504EADC92DB03A30F11CDC027BBA998CAB5D6B0EDECF97E4F175835E853CB5F55AE6F28AEDC5F6EE4987CE20FA12CC5C82FAD025477C21555F2EF5FF8F9E6F27632ACCF9AA4F03CD3880896A53C28594FA675CE312CE927C83656AB5077DFCC26959900A7793FB16E8BB650D1BB88CF9A95ECD5D1E79CF6699D606ADC8FB82DB62D8141B4E6C2BE07B4066E6869F8232468F9B3901E5683633647C779A5FE4C2096A5B3E1752EEE071E28EF22518FD36B2DF7E597F6E48F06B5BEBF9240EED50BE24DC3F72CA8402CC23BFB641E83BA4EB8B1C9A63B7DAC78A388E0884F82C3D283D1A9522FC17937D52780B2651F19C99BF1147A39FCD16E01398E2D28336B20CBC729DCFA03B13E81911D6AA35F4AE2F9F876DB5B38A4987B215EA2770FA9F8BC750949F365A3FAF97BE41AB6AF543F1A63C1F56501B4B1EFFCC5A118E8B9CFEA3F2D38D72670BDAF491700E0824B1536056ED5F274B4A73D94E4BB75B91C532B2DF3ECF18ECECD1B3F0E5698A117334215A0F97251F74F9EBB02183C12F236B16EC099F94BCC16F9AAB569F16FB27981139F993717FD9C5A08427DC25A481386FF5619B9B4525EADD84086EF9A802CFAFCF236240D22F483FDA8DC25B535A2A69A21C7F6C6C0DA0461CD136B99C2FD46B734D0AAD9BBFB88A93D63816847CD719EF3810A5EF6A490CBC5DCFE94F3021A4991E42A620ACA80676DEECA215B85012A62C1EA1C09F3455EB2E15850108ACC18C28E5EEA2D1788C2DC58160001CDA5A9493BA7EA150736C14DF9D9D49D43CEF841C2B2E11AA1F4C7F182FE38E5BAD6FFFB5AF198259B32124F58A7603B920A55B3718FA7625FF324E590007990CD9034B44A47A813058959D4453700F52F48CFC84C37F8EB70579920E34294E47D24A393CE77413D628622339875D4A73584C5BC4C4E41D2568B5111C22D3B0CC8754EB250087105B60527471C4E68DFBFAE20298CB9A9EAE78A242E98BCF6114DA0FA2DF50ED650E1D710003E25B9E5F0F3D9A5E3655683BCFF3FE7C07D3E02C119813605F1D7726933DBF02880DC51F792F7A92AB78F422B669B9D309284112950D04ADF14AB870E41F6C4B3CC4926B46C6CB6D34CD7160AF6C14F5E9CD4BB75A4BDA8087580689188FABD5BCFAB01C23E41E9585909F4F451497919F445A1C5E3A60ED35932D1DD58D0D76E635A1FAF0A03827A38AC4F59B9ED8863D9C702824FE705CFE0CADB96F32A2A59113AB4C7B4AB5A8B211B96A4087CFE8C37E6159AC3D5D588C5653C19E645A1D56093863FC50764EF1A13872909C9B103242B64E077762AF64EEC28D202F4106A818E767554BE7C1F69BE77B96E6CB6DD9D03CCC2B60A842AEA56949A46CF1D9628E60265C63D855385F740D94A9A0D73E4866EFF08526C4BD6AD00C30891AC463E93B93569EA7368C563AB596C32CFFFF55A1F1D1FB6BB08C4773E8F077AC675A8F5F62F3203B28833804C1C7967B8A63CC1CEB70FA5685F86ED6B6FA32638441D1FC9F512CF93B398B3326EFACEDE35B61C38A5DD2749494BA3DDDE1F3C2CD8CCE0E7E306E4206A9D4A2A159785B765F545AE33571E8D885F907ABC3183B40DDC920F4EA2BCFA9D07BDB4F4001B1947D33ADFCDB2013E949F54223E5F340E6C139E2FE2E78EB231125FCEA93355545666D2B6C2E87E17BC86AEAFE2FCCCE657193E1A0083AB550725CA22B903155DB260AFE9E7EF3F570768130B1D086C3C774C46D8C1EAFA5B9CC2E864853C8D40C277DCEEE928D553BF4EFA61E8E8B79AE32A23213FFBD6319821F5C6CC5F1AD81DD666656D85C164B10AE226E68DC4A45EEA422617CCEBC221C9C9FC0ACB6A9245E0B3383BF638A8ED648BD5C766E38AB276E6E921E607CA0407F0F92C056FD97F1529E5A4B180AD6A4CD3B87D525961CAAC4027037E17877D67739CBAFF8E6BF1A6C3CAB4ED5BD551B63D96CFF827521AB25B1321149DF0FF7FF102287C17878EB55CF300E1392E4C909FCEAF19168285DF9F6FAD6F1A436271689E77940ECB5B87E31C321CE83AF7B80542C5C45A2D1A68EF1B8661138BDA72798014C91617C24A3529732F244275BC46ABD855CE3C9B7C56239382136803E122A790903B0661D05B0F7B63A321793F3C7350D7A24CE377BACC715EBC104F64FF8871B995602F95152788B23E2DB54C6F78F024B6F054495AC6D0540AB6A49D76D0E16112D5157C6BDE5AF2260DC8007F16FB250A34517FFDD97A84D0C669B0A4555CD7B475F5AFCF81E4C212D5D06F742E7154E9E889BA03568EEF3B8C7FBB1B75509CE05CE1DBF54B7E7D473884BF8257EBECD3EFA18F6CC63D8BD905E0EB3048FA347E320338008E2721D9DC15C251538F1A47E56463290988A75030F07ABA86FE96D65D6470EF310DF90E7FA8C6DA9F123AB6B1E9E8C2B433EBB1DB10AECD7B5FC39C2BD93BDA1A64636911DC3FB3A603658EE4D4A00C1E09D0AA53DC5CE20BE9E6DC3DDFBDFFA3D4D19986AE644A4F11F7864C747E5B722E9A159CB87184D6B4E84CB179298F84E002019902D3979DD9FF4B4C3ACE86BB4ABDFF54339B07014DF2438CFC913049F7B715F1C1EB85D3D0EA90531ACE475302E956D77708ED7911E0F933733FFE77DF68C805C12921843A44A83933C82137A7B42110A89A96AD653EB4E5F457FD225ACB46F08EB4D4CDFEC77CD81C2BA7A2AEA4C0B08F3C1840D6BFBA266932064DF2BCE47A530078ECA669F8C711468EF0B59874E0872D4E174DBBA8A17E590AD6C9FC90895B5ACE280CF516E70905642A314E31934AA75E74E4B8C521F7697CBA74C259D71C3617EA783A0FA9C5925FCDF00F2BAD9AF2BF424D013A6ECE54B99F9A77B5A86720AC58323FBDB97F2318D3DB4A8F373C22FE015E12A9566F88FE497DC7637B2B4A2F831BA28F02F8B3F8C44645DD86AB08E4A1AA8BA92C5A2837285A6BBE0C2F5ADB865F354198B3BACD44AB29E78CED8FE7209A6ED0CE4F7A299377763BDA0C3867FAE0811F231A419F5F444938DBF721E9CADBC38C3E7C7B16620F5200182F610144D81D166CEB0E8683B735838C40EB28DFFFD906AD83A2C3EEAC71425E83339EF8B1CF2E9CBDB955420CE636BD4E081EDCB971C29E375947CFEB5DD3247A31645A8AC5694A5EFF0E509DC99FEF21D560E5A7A549669BAD50EFB4C97D8B23E9B7BAB9B7E27E7469581ACCBDFA6900C18DC1AC51653E628BEB7DE1963F025FAAC20A5E37D299A02B82C9F5F3E9FECBF497C43E22B861BA3D4241A29263F168F6D6C3AADFE023A297109CEA9571E61C400D2626DAD654F5D9603710885652159714933FCE029B8D39A574B40D1E3E9BD3192681E8D3C6E685CD8D2A1C2067B00738E55D0E88AFF65E48EF78621EACBF0DA0B3931002F65AC4B58DD6E4A9C17F9B665731DFBDB61D587BF5B5C7D2ED530797B7ED882CF25049DFE55CA1F707772745F4A1FF691135BE0158E98208F1F06C2199D6A5963FCAB81EED458627EA9D6EFED8B1ECDE18C928743146EA74E0AF57768C4EE7E149EC8C0DD10FC734A4AAF77395F1218DE8787ECED7110DB2D8D1F20F55AAC980C82F3EE3A447EC670CDCEA8A62D0F9B964AA737EB040101D7BF686E829A1E807874BE31A2203FC1B14BEFA67B1BD1BF8A0BC417FB10F69116F7D6E515A5E379A354D9E52DF112A3CDC128A18347F3BFA73E857BA8C469AC4438CE5649488E086860B9501312ACF488BF612A9EA4FECFB19F354CEA5FDE5D1033C843BBA3AB63770FBEBF3582EC13D371B521730087EBA243C3C7A57BF0D0CCB67BA070A7DD6E22A8ED43BB63D3E12E7192588478DA7CDD1D4FCF1452B6670D5591E50A06EB078253691AA6E3DB87179862F4E28816358E2BA2BDBF003845FDBAA9248507F82F856BC9385B2A2C2D8324DA71794FAADEB9FCE07FAEB3CE08EE969AE9B4FB91243414ECB0A12B9CE6B1F446A4C2D409470BF7A373E876A0C6C6BB346D05CF0319AE46E5B0FD58A19CAE9C33FFFCA146056DE9DA21C92A08B3AEF8125FC1551FA979F6537C2C605A1D76D3FBCFCEFD60212F571A5FA969040DC2D33349956E6A0426CC9F1707BCA2ADDC77FA85A5E4275F3E577F1CC09C26C59815922B2D3401CF11FACCED843118BDCA596AA86F119F5038BD724BFD91AE669BAFEA359FD81D097E8F480B5D5192C0A99E2F9D624988E5C7B3AB7F6142FB00EEF36164438C127BF2E6E98AC2A2DE6FE04E152255E0B34ADEA26EA608DF13C71E9358BECDF10219083CB3858182C57888C66D7FE983F72EEF42CF7BD3EB6309782CF4B3B803C894991E76F8A4D6628384F2A477DCFF73D761BE1D3B38DAE6EAB46994BC4A37286F5E17A9EB51A197FF669BBDA2E38B2902D4FC1DD775027C49E24A4395E452C6CAB22587C7091E46352F606B2288F72050C34BE2B2BC9A46CCF0805E6C44EC7E3C7D20535F8EC24C22213612CC61580A2361642DF0D11184D872F7BE5CA32A3088908A7AE12F2C9492DEC8C2C1A0F4E0E83794565C3F27C8B67FE1A89B5E50B13DA5BBDE60FF6A4A100D106FA57FBEBCD696740B6E508B5E5074A378FC1BD29D444E3EF8AFD5CE3F4C5504C09A9625341D808F168A82DF8DEC63380ED5231C9EFFF1834C9AFEAC9164F4E2CCA6C19B61122A8101A44C3BFEAD4C9D870F807BFCC5F4BCAE60DA5D1E258F735E50930E72A01B5E46EC67E1871FD5A351A9731CCFDDB8C92F2AB07CE21667FA42444CFDE22C09526C85F65618E919C33E8F561E381732B36400D8A0586DD801D9FE8A74719022F3A2C68E83DB336CCE5CB091DA25471EB831EF22212FD39BE2506FABD96E2F80D4035777B5A173D52851C208CF01541FFBE1B0135BDA8051282039BD53EF90E4005EEC512471A427960BD80BDDF9BB8634509F796BC750025336B45AB4716A29ED664679DFFAD5FCB9524A0144F8BC80F007497BCA0B37203DD0EA462403135F1FB444934FDF82F9E4BBC09AF7C0AA8EB1E1B1E244FB56BFACDC9194ACE3F8DC8741E3901B190F28B5D8C0E725B8563E10D0880110D4E3413F4FAE4E6F05D4BD4A6AAB6FC5465AFDD722B4FEC6ADB973B003C7FF23330D8B3147F1544D1988BE000B9992F699FBDC0DADBC9755A73D1BDA8A4675C30010F66F245F45DBF4C797153053A0D2114F0FC2C17FD3C79DDDF45E2ABEB591B5CE399A4288BC38A6B5EF32678BF36C9C1EFEEBF65B34DB40F2490D416B4F3D61A446D118A3A39DA7790C0F3DDBA0179C32CA24130B3F88DD6D04CA0CBAA2C84DC5B7C2751EE209BC4960377C33B22CDA22CD31B2E126B6DB2ADB26A7D3FE33AC4F9F86D0105B5DAB5107F56640556B4DDC1B6F6E3DA5C5204D3DC7BDA1294F25DC5F6040F5147661928753CB11501C665A6770D5B7B41657D267A15817A88777CB8C66A0E95CAF7ACA8B53C146CC7137FD410221526020A0D5500D71EF3F105E64D4D970383C2E3FBDED8CBA92FE8EACB2C63A845FDBD699432DEBD51139E10FFD89E0BEFC3D258389C8200AD40A53BE363A0A91F7EE9B09100BCCF561980104A43B9FCBC6931EF31C734679CEF6A97C888C5F51FC14DEC88F1A19E83971D37AEF47CD197D735186314CE41BA971197B7CA332055FC972501ACA4B49537D22619BB001390249C89FC3F5A32E6AFC5E2263325902F3B2C5039FE5B5F6690C4520B21F83AB88F88A80C4D1FD689546307C5783B7615FC71C8AB33BBE32EEEE442E307180F17894440C7EA5FF4507D73AA50C12DD46AE70C923FF2E160D408F401253AC9732F281918AFBF792456BC7EA852692D48EC0D8BBF3258EB0D56034987226F9B9FDDAC42688822897EB067FDC0EC9EDCFDEA41AD4CC90F4DEEEAF0C2A1770D89FEEB418F89A2A64D35EA72C15372C6B5B50EB4AA8FA8FB1899AB1C91F996576520EE88D8130D09D14A68976CAAE162F45AA4886557305FAE30873EB7176C999E29EBB23DF84B2806785BFEDF6C86188BE2889F7DFFAB21836C3A2A0E839A4FC19A33309E1620B720C9859A20078498AF3BDB88BED556B96EE0562F93355D740399974D19EDEDBB43C47584FC51D2B9E21705D18780435EC3481BD4F8379EBC028F4F5C321050C81A494C2B628F44B08FDD77261501F5D3747F9BEC87B67DB3A48D67D4E2A294A23FA295DA4DDE6B44B08DF8E4B31CFC13DC8C6B7C38D25E08FBC8513FDB0B3C4D38AB826C3EF0AFA343E694C8009F9A4BD724296FAB804B71951A2BDE0E6F6ABAB168273E4096A712581EC0009F43FF8E0EDF4D92868AECE473314C72B7A458C1E9B056E8CBD4B5E973C276A6E703AA35DD6B0CAFDE8AD704A32466D59B924992E67940B0BB7BEEE781F4E44073DC48F954C097C4EBDD2CD636A69E5BAA60C88306D8F40E11B5C7064BFA5758EAEFF3F93A0669EBB90C13D6D903DE4305E29CD0787F009A06B411BE92C3DEC055A857E441EB037F10375BC29EBC1E3AD828221592F5D56B245FFB020D619CF5325159060AE3FCB8CCD4B478C80018FF3856DB4DD69C5B33CCC42B2A1E4DBC19A4D83CCC055337725821542953F1AB367172DC4FCF8E5884E2A22521C7F219AA296C99E62244942389405D82F411EEB973432F0FCE53F1D6C877AA1EC9747FCC5E71973EDF5AEA41EC9216C4E211D1361D0DD7B1239831454FCEF6936DBB33079381C0B01E66F863BFBB6232B94C946454579000331EB186E6082E609FCF39B4F3FA6C6A49C443532E5C0BC411884592A5BA56B84838B2279286255076098A9174C87F4683DDC11543DDDD9B6FD893E960FCB716647DC4B9B80BD1B0D6BE65520FB1C250031AA9E458C5F41814107BA288659113DFB0E76AD8232122845E187BCB7A20EE4863ABD8491D1A64BB16903A2782E49084DB69B5DC9F49ED4F8074E6397213FCDA91D036F3D5189D5C4389B3FE6D1154FA32E0AEC89BD24CDEB941DA0CF40D6B1ECBE596E0A91E0D47F5CA41DCC9581D8B217257F59389D52EAD2AFF45CC83C3741E8BAEFAD6731A761A7602909A73C1D5BF9321F217260123E7C9C9B1ACD755C7F23C6506A98750082A01E52B15FFCD72D3E933BCBC1D537D98E6536753F99BC64E63E04E5F6A8ACD5D8BB8B10847F4FE55D4CD32EC4F6595B02F229F8DAAE4A05E6DFF1DDD53EDD8E1F059D0B4DC48A15DEF249021BACC8240F4A64EAB1D8BCDC1829D46EC38BC23531198770DF851B11712D89901D60D488B7530A923386A3A1150703AEE9865D0D7D3517C333C9C471C6E1092498D51F5D5E31A7EF285FF5CEA1F2A7D887DBCD2D3344D0F2B305E0F5C3123F5E0BA94B7FB1694CFE045A8D2E3295E00AFC8BF3AD8F31AD69C113E0404592E57E937E21A5C96F813F7294E4FB53C334B7470FD790FAE97B6DC10CBCACC24BBA93473BFB3543DA39DBED57568271846DDC29E48BCB2A76307BDEF8993572EFC155738BCB384AACB5796A6F45962A1F1BFEFF83561CD3F6D2915A26E8546F9D4FEA55F86D63FCFD0940C1B612FB7F47115456FEC7B931007605C7677C30F17A9E47D2D0F3D2691DDDC5DFE534B81BF97AC61F624C008331104DC41C10456CA0EED325DD48495868C2DDDE61501870D837349169DEFEDCCC1CF9E35C53CBAADE9128A63018DF0C12BEB75B9EBABE5EC414EACBEBC6A567D46D6C1CC33A3B0C7A33295AD6260BD3F5BF1F06202FA0C27B2CAE0FCFA639DCAF18A0ADCF30DA135B6DFD70139A19B3155726C80047E6B41757A328733B6AB2B2EE016A760B7E1344E8FBCA0B801DA6CE4F9EA81501FC6E5A702F55BDECEE43C8D3DAC75EE47DEEC4BAE53192584C5BD0853A06A02C4054183CB1CF47E171BFA49A888714942CD59327A6D7B31AE71C77052A4A511CBAD1B23201E4226C804E99CE7E6940137970EE5C50E5B9390A5A183E14808384F85E76A03BE3F96F48B8DAA0D1FBD8652FA871D501163A5E20D1F69BB25D51EAD030E9CD6EF72B31A3B5840C1416FFD7FD51929A93651119FADDE001B3B542F60CA3C7692A152B2FE4828042C6711AAB3BA40674554803C6C613BC26E10ADDBB6E1B7DFA2D3460E6E6C99E62E80A7F1953204F9FFB12A6F3EF71AEB1A873A18ACD72B9BE6C7A1DE1430E4D45A935C9409BF6B6835394DA2F10A42A02982FC26BE344FAFF63903A2C1E6747C4EBBCB2F25E9BE6F180CD28A99661EB9780CFDFC267AFEA011E38E1E051929322939A79C297A7818C864CDE4318A158EA9A6333E2496BE72FDDEDF486EE24AEA0E521D85321B2B99BA1C5C974DB1110A9AD751B7BB742569B3353D945E2B7DA5A28B7FCA60971F845891C75592BD14F1AC4DA21F65B93A66DA5524B0B552B4FD5A3F5321931607483F723140AE5EEE30E999D63B5FCB7E07133A96851F046E01453E6CE0475A8FB619719BB7D2E40DC986416C7A2996A6EBD36AE27565B6874DEAD2B5BA0C019E2BDC39DAA04FA743CA75EABB02EE595851EEDF8C8C2C0001BF63015B27E8E928E2517E1092E3525FCA70A575AACAF4B396212A2E6AD7B8B13BDF5EDB890E356F973DDCA7524E1B67841064FED438F4E80C02386A91C409A99AB0FD8AE359B1F6DA0D7CE3BDA64DB7C12C9E28DA3ABAD6DD23A8BD518F525E7E4DC07C8F512A002A2606712995A2DF2F9F5B898868FE312465A0D93B55AD1CF6574E29E5731F72ADE917DC99CA0B165E9A0247EB9CDA02B96D08D30E8BFAF15C79CF7785C6450E16621CB07EE0268648028285B0F0D2343B714DD069368C5F765F7F635998BAD0151B1B9A6FC111440091903007AFAED25712514A61DDBC182A409022A38E14458DBFECC1A38EBB1C94560DC1C35C61FF6B4B86137DAC7E91F095CCA29AD6FABC072168F58BE3A3DA249F0005714EB13A64B5E81D555CC72FFAAAE1A2DF6D4685CB38D7CDA97E7A50E910C01D7DB146B51EEB2DA2370CE32239623C18AA04014529B115287B080131DFCB23E0BF28B90C698A516A2FDAD49BCE538D20999D1D9B670895474F56181E7B669F796924B2BB0AE388179F397DF42855CB3CAC7F961A6A80815211AF5F1B32EB7E6AB05960201640242D13EB4843CDCAB68B5DEBA35B6427AB0B7C44D269E7B0398392F1CFABAF01275A566840EFD0565792EFA796EF397371032EC005B0521FCC52491ED2CE46B9B02BB1B2B5E532F3231EAED568746402C22ADE125AC0F983CC6C62BB245A13949FB0C3D404E6547881B9E1F52999109EF51EB4CDC2C0C45CCF7DBA86E82B3FB296A2B694B754263FC634855D941CC7E6D328EDA17CCD5A8F1561433581916C9C737554A9EE75C8AA398117643C0E43E39916D7A9B77A4E79257F3A84FF08594CBCEAF85402A7621ABEEB471618138A42B5D6229A35BD73EF7941084F9673A54654B0A5D87DF2437FD34B54EBF3346CA66E11B27072761EEEC7612F97F896E25A5ACEF8DBCED7774456A5CBEBC6AD1AC2FA6F3358A2A01817754516E6557F5B7DD74D39160DDDD1D21A60004748FE744BDCFE4A7DBB1ECCF133719E8D4E4926E88EF2CCEE964C3F944FA8EB2FECDEF7CCAEEACE04FE3607656E9299838927885945FD89AD3893234DB4A4293BEFE0F2E519E51F88AC0F151F1629B2BC6CEC0A87B768D492BF37976C73E01C2F0A975A32ED9CC972451170992E9C60533FE705DB582909FC55E13C8F7487C92B9C7A696F2D14121D4DA42BDB1C0EFB6A9FAA82C35059EF6FCA19D6C19360F95526D59CA3831B80205FC31F59ECBAADB7B402618EF002175CC08D26DEB69598E799C015DAFFD4BB224062C3688B566AE8D001C05A41C82989F03CD3A0AB372BEF7A07D77D3EB2E0597CBF98FA4B506EC40348E2B685F9D65D4C7811E8E74E5430810DE969FE18D5B93D06D5B505C70882D0B51A77C7B7F2B7529C9C3D3831D660EE159D74195442317977F79078FC51A50B48760C884B2615AF00D3DAE1EE3D758F30D905B3892106E46EF06D16C6A2072D7A99453D0F4574FA5D109D123EB412EA27F5BE111645052AD7C025191F109AE210EE22515633D952A44E0682B262BD1CBC7DB882F721ABAA2E38E0D597C1A8AD65FDEE5CE7E9BEA9CFF85F22CEDC8A5D0CD0A87046900A4BA2A760773C509885081A7815AAFF85395A27BA4D3C76411CEB323562E899E1183429E05F249F357B1710BD87E427881454F26FFD10320F0BD550070DCFF597BE449D5487FA542AFDF6944756FE9480D2D9883B6CCD3BF66F88E95FCE090D25A06CA3354019D50286A521DD3C104D04CB55297A5CE7CF262A719C371A5914F84C4D62460E06F5243C48B238F75AEB7CB00D87C80423BAAD40D2D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+            Assert.True(Arrays.AreEqual(expected, attachedSig));
+
+            signer.Init(false, pubParams);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+        }
+
+        [Test]
+        public void TestBasicKeyGenerationShake256128fRobust()
+        {
+            SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(Hex.Decode("7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E47354D75735F16E03DEC94D1F5B00C213D"))};
+            FixedSecureRandom random = new FixedSecureRandom(source);
+            
+            kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, SPHINCSPlusParameters.shake256_128f));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+            SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(pubParams.GetParameters().GetEncoded(), Hex.Decode("B505D7CFAD1B497499323C8686325E47354D75735F16E03DEC94D1F5B00C213D")), pubParams.GetEncoded()));
+            Assert.True(Arrays.AreEqual(Arrays.Concatenate(privParams.GetParameters().GetEncoded(), Hex.Decode("7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E47354D75735F16E03DEC94D1F5B00C213D")), privParams.GetEncoded()));
+        }
+
+        public void TestBasicKeyGenerationShake256128fRobustSign()
+        {
+            SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator();
+
+
+            FixedSecureRandom.Source[] source = {new FixedSecureRandom.Source(Hex.Decode("7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2DB505D7CFAD1B497499323C8686325E47354D75735F16E03DEC94D1F5B00C213D"))};
+            FixedSecureRandom random = new FixedSecureRandom(source);
+
+
+            kpGen.Init(new SPHINCSPlusKeyGenerationParameters(random, SPHINCSPlusParameters.shake256_128f));
+
+            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+
+            SPHINCSPlusPublicKeyParameters pubParams = (SPHINCSPlusPublicKeyParameters)kp.Public;
+            SPHINCSPlusPrivateKeyParameters privParams = (SPHINCSPlusPrivateKeyParameters)kp.Private;
+
+            byte[] msg = Hex.Decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+
+            SPHINCSPlusSigner signer = new SPHINCSPlusSigner();
+
+            FixedSecureRandom.Source[] source1 =
+                {new FixedSecureRandom.Source(Hex.Decode("33b3c07507e4201748494d832b6ee2a6"))};
+            FixedSecureRandom fr = new FixedSecureRandom(source1);
+            signer.Init(true, new ParametersWithRandom(privParams, fr));
+
+            byte[] sig = signer.GenerateSignature(msg);
+            byte[] attachedSig = Arrays.Concatenate(sig, msg);
+
+            byte[] expected = Hex.Decode("07EB19E7D838D71EF66B8263B5D1F8EC0A05A783FABECC6B537337B5993E1F4250B1FC84E7C00A3A60D7C90DA6BDBCAB118AED9D9AC86FBA8EFCED82537F07715206398588D4C78EFA3C922ACC27177061813D52C95D1FF444774BCC2044F59139A0B0080381296D6256AAF3F80E3FE74EAEC8AC7504F53D496AAC534DBCE0D8CD0828107A392DDABDB385C8A8FA97442068177010FF2C3DFF4C654A9F1C3C529D850F80D8AB83C15C3B2E856055BCE9ADA1DBA677275A445B17E5C6C18C13E77F0E83A00ACA6147A9DD32D55989725F29EFCDEA5EACD4274A6526FE7B44B885A3E0F442974D4FC5E75AA4349062053EDBCF436B265239FDF74A401B7529B4493B03A28897280093CEF0B9E0F447957DAE5785BD34F1C8DBF2D6E8124635017E3A8FC2EA855CBC4D4EFEE16D2FB8E995C4778A95ED6CAF5F038D15A5AB290979031BFB617DA32D0735C1A728912B51480C18B58DB7F8158DD29A82CB3E88DA4913B80F713B6FD94F4F433506A1472C0624936FD2E844663DADC59925D2FBB4BB6DD57C607F786FC741FEE57E562FEB11710E8F2FE43C8788E8EFC0B31E1F9D4B54F860997C87F5704FD4305D930D1F8352570A604229FC2B3BAC5909C6A1DB7ACB4B1D5B3FBE0C4EB8A76F19196D3629616BA8590E16D6F9A2E5C7A8399A9DF9A8379CD1BA2986037F3DDF2E8081B378E706E5899F6FBA1DE8B59918B54F4299FEDDCAA79567A45AECAD77AD9E4B54EC51F92D677962C1E5CB4738BCEF892B3EABB24F8BCE0FDD57E02CDAA9A8BCD946962492FF08D2E68986D9C54A0BC8738846E8D954F97D3FA2E59C0DFEF7F98BB01FCA7359564A8623192213F70AF07DE7AC652D3A90A1748E0CB3733CC04AE1F787EE3C3D305750C60340D3E6259DD117D9A5E0B2D3EA2A77C8A96FF51982265A3DB87C81F417AA985B4EAE4BBCF120B5DCFC89800288ABBD282EBD49F00618723D4DE2BCFA36115311E9F8101ED8931DFDDFDD37FCAE6134EE4D5F0B0DDD8EEFD5A57B8029B0C503307579CF5589D60D528129F7B1BEB894AB079CE451D24EEEAAE70A1A8AE41AC9B4BA6030DBECE4E204060967DE33836CEF20FC0A40C7B8FEC57B5CDDFCBEA5B25FF7EC64959F8681FC7B16D1AFDE96895404F2400C1590B30EC44B988DB26D4E097319003211F83BADFDD85D77B058E47279B63FD5CB8AB4A3C9B4592F5509E9DADFD5488EB54579C87CB7685DAAA7CA8DBA1A320B195FD64EB8A6BF8A14C3D9FF660BEA64C564FD3C07DA9E9ECE97648BB995C39E90451BD779D4E8C5065655DA33340F750C7BEF7F10EB38425AA0AD969CD6890D0534F80CB619620E74DE9A0D3281FB20548E54245B3EFDD2C6386B37E83F5A87413F0A5050880FBADC83CE55249E591B875E6C15421537693A1F39085EA1D658E6C88D2E3F10B4252BF7F37770F36C2D132CC870FF7027FB4C80A887C5CB09B86432482D8B40CEBE93F41428C4CDCCC28B874AD81148F389017A9ACB14F32B2FDACAAFDA48AE77DF97CB060C058E5606CBBC7903AA81144569A30647FE223D0C054A5225F736B7F8CBED49BC5055D194A75BE438FA2ACC566F01D15F868EB415C31A255C3994551413C1A69F0772AD996700D63963A0EA8F8EA72907E153A8DB46D9CA8C7EAB8D0F7AEEF4FB3207992C283A82E0EFD9BCBC48BB89790542F2C5FB0B355F50D0E8B08797CA6EA21821292E7935303B76694F0513624BF084A85096953B1E63A5E4E2E823AC992B3BD0B65DBFB0EBEE721FE50EAB488C114F9E626911C859AE8862D7748233BEFD5C14D2278B1C1E1BA5878B56835F9A5A1DB600249674A524F34549B30307ECCDE8232D64233B16AC5972853B20C12981E604B97B8A28D9A5C3F7B17B61B5B241AC5F732E15CD5724F68E98917A9E521D2DA629D96D4A373247822CB10143F3EB22D7B4CEE23D1372AA0DD303BEB438671217120BE650BCBD5A463089AF0249C79935ED092F1AD32FA344D70990774E963DBA60ECD4035A619A1724D74AC92AEE2D4A5F7CB5EA6705BD35C2F980BBBA4D9406D35A95AB91B41034544F3A1C39EBE72138A5A20F552F236CBB412A678068FCED57146B68C32ACD8F32E2355D2CE9653393D9B54482A171C227FE5CE4086F2DE75E21704612B28D48AC71C097E60789D123E10DE496792C25249F9D5D080F716B7A3E81028C22A7518FD08EE597C503A4661C6E7BB888212E3AB7642CBAF38069D4264050452953137355CCDC324C248D15D036572A57B4469826A655616F0630D70BE421B5F2BEF1D2E99DA2C926EDEAACA7690D2AF8F96467CF6CF6BD783BA6627C47B19CDE9FF5E0B94F6C718B7B768A280874A4E94308837877C011FC9CA502A080B7456DDC55450C6EE9249EC5AD95B8C765A5F37D9A092581EA6DBD168B150F01B5B9F468485435C6327C7D14D623FDCE5DE03AE6B5523708D5D440E938DC078280CB723A9A907FBD49078FC88C175FF7B7BAB3FD891E93C7B2B310252315B76BDA5FCA9B4B8617CC96E28226247322B32F0BBDD4107F8C8AEDD434B7F5824225BECBBD8FF2F247810A73401A0ED27E906253B8C285AC02BC8C713C51C50C63ED1483EDB7DC8540F01DA71BB48425768BCD63436909E769023EFB947802B1B276E60764057447033F7DCECCF9EF4197B176F0F137718BF19F69F03C0A2EB10CBD7A1F3ADCF0BA8B2661BD37B766B24FCE47695647FFBD08B0BC973E4C0D109DB03E9A4EF447BEAB422EDD274590F597F49B64C6D715B4C24810AB8214EF3822D8370FF120FB822786604D4367855C600F8F9586209F8681FF2515175B09853DA5E0EEACE367B1B234E9C0251281E19EC5ADAA6C7B90B81894B0217BE7D9FD7CEE621B6183B602AF4ABA72EC17C9CA89C8B6E438319D9B6C8FC1A840BC9AC86E49DE7A639F9B430BC39F4E3B5846E99F18DF55F41396018F24675A670E8BC6AE4CE16D423AADDE3F8C1670CF16539CF7E528F6A3EFE0C944DE6F2F66B30753FB19894ED01656EE9FFE46D83559A4610E62B291C005F863C08B90185A343E423B6C65A25B969152DFB63E37E8143D076DC643F332C8361E2105E0ACF06ABBA6F0AFE7CE91724E25BA4947A3D261416FEDF229FCF6EC17EBC8DA963EAC0B0466BA865750CA7231ECD95AA9A387C80A10DF9B0E9F72042E59A70125AE456894AAF975C5AEDDC30CD811C366C83AE6F72BD90C092587CBC4291657908A746D188106394568C4A71E1328A4695ABB03F4FF718BE3C9E10CE8257FC7435BFF187A3E1ED0F8167CCC503521CC6F4238CEC919E56BBE0D48D726B418A7966760B2E49600E8E485B3ECAE955CF8D198760554682D6A1F268744D6BEDBF56D31D90C75861D35E94F6B4C0B416B7D6ECEC425FA044CD5AAF14F90B9DE9E51D317FBBAE3F44893269D2A04B87A84C606A4360150CCBC33EFA0F30699F62A6F111D503D8D373B511FB91FA9CBBF55CF4B62C91ECB4BD5657CD86A52DD62927189C3442D5299EC0D95893C4A434BBC48FFD7F63A04547705BD4C2DB62E351D8C32CC92C752A838EE21C0A1EF8BCB3B48557F30D6006A48F77DEB7857E5ABCE652B8147F01598C68965A63033EDE88720145748991BE39835DAA7B1B9572BAA1C79E9DF9AEAB4DE8CC31B1EF86C984C4C9E77642F78DBF982B280CEF6ED346F66B10C7EFF0ED910B2DAF21551751D22F399A308C4C049D5CA7A11377A9912639AC62E198F98DA101A3EF08148915C2D44D51F52A6033B8049800401D4F04032FD12D09EBAF64E6A1BEB0CFFE076217DE8BB8810C3B2A4280C0658BECF30EDBD3CE31E21DBC1440E1A3D22D2DEC5EE530056D7BF9B4B467AF4102BD95FB64C972B927DD1EE2AA5992D7B35CC54255F9B53B61B730CD45446E6D6D90D9E839BC825464C7C2F355D59EA87FF16187FFF6BF61FE4DD4909A83BA838101C4334ED402F07416EF7EC63EB12E77077D7B9D874E423542446A64B59DDD26BBBBDC25068CD4A3517C0F7852EC6EC5C8D42609D41D44715424821796783A20D40C890C211EC58F7D71F62E5B10C833D20E0F0B53CDC6DE322028AFCBAE33B807945723B572E94EA65D45CFC0157F50CF33ACDB4B11A3C5059A71F2176F63BA894B269EDCE3CB560C7308C44094CED7E037F4DB7D466FF327D61B800677637DB91AF97BB0CE8062399B83ECA8985AB6A03AFC59A8F5D82CADF67AB73338D62BE6C3BF3A42523345BF501A03A8F008F7495DDDC24750FABFB0B7FF80040C272F862EC0EAA03C24A7E161E1CACA3B61F682D22BDA9536A2349CA8767E5A424401DFDADAD82992C871441C9FE69B977D18C6E9728D56D287544493AD02CE0C793BF1D352D849B368388317D29C6AD453DD520A9D6687EFF83657768B27E052E0C66DFA1D30FA1AE2B906D1C1A6803379782342BEFF2A2C477A1E0846AD8E30B94EBB9A4B62C93269B5F644D6085AD7C187A804435C3BF91677509E3838DC5643C143D990D46686770826FE800835E3512746A76C600CB623F82FEEEE40801DDE32C29289FCD62681F307BF04F9B1483679613B6EA9E01831FCB8C205C085E25DF70B305465D05D5C12727B40BE8F2F2A3F6A3EA105598D35019C1A1E97830614A44F1BEF4475AD5250E4FEECED11C3462A0124B57C22946D21BE1564A0E1BDBA587DC1BBB59AAA775E148DF7643CA1D0011F0F1E9FF95BC90B8A3FA77CECC69F27BBA2024CAE000670A34E566B95F40527C041E6E98BA35FD083BCDE8EFE59E6CDE39F3CD3A61DB7C4D34FF17649354641829FD0AC968BA0A9EAE890C8C5FCD810B6289A1966221A174366ED7B84044393C38C021D114E6C0C336C2D7330A6F2423FAB528785F5DB7EA219884A267054A2753EF14E8215453324D069A9FB719B345FDC564C71779792A2932DDBDE7DC4058F0D57ED46F0EA1F30ECA9A4D91D0C54D7D57025A502CB6E3FBA0B35FC8CBB2AB4DE6288601A99F945E1CFAAF743AB92053D42929B2ED5DA7AEC1241497BE6A3FD370861882218473124BF2D99207C4E69DA9C0C8FEB64C672B70D5D5904660FB9F8E61E7C7C43D2FFF1D65924D16B34A6D1CA60474F80C4AB6ADE54733A6F63F8EBA47B301E8B541F33526E62E07B6113BF4E81FB66AE247BB37237B07685239FDAECAB616F4B4464DA25362611DC43FEB0B08DE3690125582662DA8E15BB6C66995F343CCFFACA46C3FB1281CF8186362D62326CE9915EBF6F05F4A2C7BAF44EB56A81159F7EF004C8142C7E6D5735700B0D31AA6BB2A4FAE4A48BED0AD4CFC49E0A979A973197F3C901E228B6985FC1D455C04D5EE62AB8AFCED12F93740C2090B0E6773B7B4B4E5B7C3B415898ADB3FDEEB92846B635F49F366A869F35068346038CD89D1E77595BC5E5CCE7DA6ABFA6C50446C3AB8185910C1F26D38D109F1FC83070DCF0E75D045A2E9859118CFC06A3880FCA5AC19AFA6DB7A1804C93AFF20CED63C68796E281DA7E69962F3B1A6488B99D424C53C706E9BB289AA7F02C609E4A643595312723BDE481D47790BA89C5908568B8FA5DAC6CF604D1B0EBDC19446DD5A49E79CE8465DCBB82562A37CACB520CD6C396401BE1B856309BA6E1E3DC80D66EFC82B59D10EFEE68F191140B766FCD00F9F8B3A45BCAA9125B62931F7E9810DB4AAABBD1EF340F3BD543BE473851B63E8761A93E76FD34F465183AF97D67DFC9FEA886E0E0DD5A0F77C70F72BDB8E8DF01198024A96E635767BEBD9B1B4FB475428E18284E0C3E2E376A43D7ADEA613F72574955C1D257136EB6972E22A439B3E4175885810A6541F9C374B4C62C4E76AE3D7253626B672FA407B5D2616EBD3D44615EBD764BAEA2ABDF1DC891B6F1659D5A21BAA63E126059D6FC7621F99B18417C192DB54BAB2F8B0A08A4C8794E2CE1867C3C23565E2B92F1EB8C61F78EF90E978ECAE8470B8D9B2D06CF50750D1BD0B4CE8AC5ADA8CFFAC80E3DFC3894D6951812EC96F2FB6225E2017024040D256466448568104514C029FAD79FF5D255F50E64E03A22A0E7EAA8992F387AE55C5E0415036621F1C29BE60780394E5536C5C60C435BAB23522CCCBC413AD9965CD5BC02A2B86AC50C3279D3B497F545E10B6F7F90ADA879E826EAE19F4C564E57A319765D189D269175219439EEA40DC30CC2EB0A885DA1553C7B68BCA1B50AAFEA41678DCC55E65FFADEBE7D9336EFC5FA09680B231EF2E0F4290733973F724AAC94736F54FE66974D155D065F74D8B53376C7DDF3AA5FC10E30CC590C727C48857F7F2CC4029642B239868575AEF60F63A17DBA23DB676808CDBBC091E42CEF112CB77DB5C23074769AE0D46112842AA179EFCFAF4D5289612FB67558715772A9C9C2FA66A7F1BA202BD0292EB399B3256CC21268F5889C39E1E96A7C2848D4C4F751683913B34FA706367D912B704631218220862927E2C970CC5BC17AC792C0AE0D857451A63E04FED30892E0FE05A1474C891B932FF60208947AA9100D58BC4FFC3FD40097AE75DE2ABE0CA00DE0C82DAC4F3C6B8B7E26501FAC6F143348E81DDEE8C4FC492A7A6391225541CF8D6334A3CE40F7B3D493E1F59C2C414D5109FB7BA2817A3D144541A526D39143A840DA4DE4717203A857EBCAA43193D079126DB7FDA3BF9B87FDAC3ECAD112FE890FA51D8C81C6880E7B8763AAE7E2EA175B005D6ABAA5440B37B140CA28A45E242D787B57155B21C8A18D598BF4518469B72051287459C1B935DAEBAA6F1F42D5E74589737AB070A5663D38D89E6C413F2AB49F63CA3A4AAE51805DE231DBAB63F487A09242340669D0D0C46FD544AE2FC3E2A4A78EDACFE735230032902F1CB5573A822CD7A39D427C52035264B2789C2626D1B70BC1D6B590610C5AF7631F461E18294E5FC9E8DB2EF4D5C52A0A35A5ABC978D7173B511B7918C8B3B5EA8A2DC7DCFCEECBEFA8A67E127D1435075E0E0D641F4506AFDDE9EB2DB6C4B1489BBA99106963E851067DD7BD9CF4A217436081499448BDAA7D063740C3E726D7A4464C924D4AA1980C6BF4CA91F2DCB837BD5818C38E584C24760843CBE02624792ABFDD6662AF45FF6ED4BE17ECF716E68B06F3BFB29AFAA41EA8EBACC75C054733BA10B51D891FA6574AFBF7FBC05E826C03F6E48EB0E40FA0A6CCF8F3912B8EC05ED4B4586CCF679EC312159981C6C54635373E71261827BF7DD6DB5D7B9290CC9E291F25858CE901DA54F0859B6A229B9C4065798CAE31E111B894C33E5CB3A2D438E0236BAE2F28E345980EF78597988485058C87458BCAD72D524D24C0EA5BC20F2CB2353C039C95894BF4E74ECB50A3B7F7168345471971C15707F874A43DFA969CCDCE83FCE98AC45C541C5F74E4B7DB788BE3AD198CB0A36C8C08269EFCDA647C2DEB37C7B1F26759BA3DCD53142AB6E2D7EEC98CC2BAFCCCAC6E18319FCEEE64089DF803CB222D9EC0743EF8C569B711715CE46A6A0F9A141E226D174A5ECEC9F45E630BC70F61D82014EB79C9D3DE447826095AAE18496B494FFA810702FF83D1621459F26B9922F8A9C67F60362A84B96B6D51509229F70D181A196051EFF03AA69DACF31C8F1C140D3B9D748A5523CB3A9B645D886DC90F8455926B739B6749BBF24A826BB77B7DA80072FC517CEB74FB1AD099E5D9692319BFB66EB303FEE1120D5A26C7F00CA53FF66BAC5BCC760959888341DCD6154D68B02A3EFE35E5C217352632B1305B8086292F8248BE48E9265D287A5D14930FBF3FAB540EF98EC06C0F7A5E47845A0D57485208D0DEBC82C179145F4EF31C02381A8F9CA64B0E86048190D8F0E085D6BFD1D4395300DA250BB561D1DBAE4F6496AB955C511D125B64975BF39870E317DC8E60FDC9F51EDFE1EB73B573A6330764D9477D12A08A38788D1F4754FCB9F6847A0FE306C65ED18E79B58F6EBAF165911C40B223F4AF4D87E7290D5589E5CD228FB28C49AA026F987FDDE0345665243530D51327525BFCEF2CD37403E85D4DD96F0D4AF1F9E809AA0ABAE0D2D817789E218B069A882E60AF45D3A3E11A7E4895DDFEDF03722A2A57F071E9501F0F0AF6B43CA3750F9310836F469A7C31D891D371E6AB86D6350C54AD58F04891854A1A3AB6F618767438024C873F67B1AF85770D0FE3AFB6290E08F57B3DDF0702C2D0E1417146A31DBDD0B12CE482972B3A3DDFB2677AD84C402F826B2787FFD52DD031434374B8167DFBB5B83B1DCE8F8C73D0D3370A0BF03ED254E10C516B1D7CCAC198C33A91062B6FEF55B26BCE3D03437465425129EE459DD8871F46402A4C7DCD7FD8F9097B5B08197ACBEC1B71E7B0BDAD10B283182B0F7CB7A9BF1E7C4377A66EE11C10BB83C55A828B332931EEEC252F0624DC59316B5133B66BFB8B6591EB6734344A4C156F4E9DD59D55D5F5822A1048CF94B061DC9E9BDC26475255EFFE3DDDC60EEF85871AF51C8C8313DFB90C82E80333771A87D9CCE4F01713AF2311450FE45A33C6FE1E68903734EF195F5975734505D5D6C00CEC074416E6D6E2C7401E54A9C896DBF1FDFDEA766F340FA1DF6205CF3A0E5593CD71E7EBC108E9654C310EA8714F0F41FB54BC527EF88395D26D8B08C5162F39D211BCD0A7EB144C8A0F8DE73878F6AB2CEB3FEFB2D50E3556E742A7E53777CB3610E08A62DE8F54D7FCAEEBC8C4E8B15CB726BF3FD4693C634A9C812907197EEE5C5DD31D9EAAC9E1E281859DEB9317EA69D469933C83CDB91A7877923F592AB70821F28353058BEF400DA4A797D57A5A6C6E512D2DD629F90F6644B6630147F8E11E4CCB255CBAFC5FB1773D3ECC729EBFD87785CD9DAD8FEC8505BC5157B3E39CBA0D4B8730BD8D99F281B133E15E4548E00526BC409E92B5260D38AFD287B2EE9390A2A05FDF5007666CCDB290D8BA48A8AAB127A496DF9518A071AB54FF7A436D5E279F4A5FF1BC01466E165B45C48EBB7F8D240F4C01967080A534131B939EB89FE4F66DA2199CC1ACBF65D62BD8D72E10C9FA7000E572461C5ACC4C6F5322AE9AB5209C0C7D9E5DD0D73E6301F127581F1BB2ECBB37FC9CC0FACF4A50D773429E6B510C8B181005D62654A23D0AE14822D10A3E19D8E9FDFE7AD16E4364787CED548C25BC3B2027AB3931ACB356FD658F2FEA7C71C801D977202357B5F43F81EE7089C2994D80328E166EAC24458A98CFCEC917B2468A417018B7A921964526A742CBF71BD8EDCD57BBF25DBC7B8BBE92C5E8DD27EB4253673EB8E760BA8798F0D09BFE31336F1BEBB2688412F46A90B2E23D588971DCD9EA9E2846AC32DE8BABFB5D8F9AE097F83E00763A18FCAE8AA6F79BBE5F1E39D9F6E9A0BABA94D7556E5213F6F85AF769CAF97A687A8DBB34B97281DDD75CF3D1EB0CED8D15360798A19F12548A3E373467EF850E514C1BCC608277E54268B12FB192B3681FFFDC05FE63C2651A1CD73E7E7CE40064943A3E6DA729E2A1971112B13F40BAF0C5AB42D92B8CA2500720803A952664132123185E05C9713606D22815F11FA0333946789B2E6C3575D504D9B56CD5066CD367328217B4ADDDBCA497472B64A3B7B7856CD628D0EF1D26CDDD50413DD992F2FB85C4919DB005E383E0A6C5DF2814CC930CE98EAEE19BD90495F5279C72D4262B4CDBB4634CD9A8E0D45A94AF30124A10494C48285F34841E5EB7659892FCE5FE32D90868848CDC1A0FC31ADF3B75BD41C542133758EA1C106BE21E28D365C61EC6E62F3699E2F2CC983BF259EC51AAB64421D87A13558D5CEDF5294844FE2A48841197FAC20C9CB140C5129AF273CA37973ED93D80CB1E09AE9F08EF58EBDECAA7806FF61FC2AF0CC1824F4570C8DBF67F4C938DB6BFCB77C52B255BFAF8D7A9A7EE86CF6EF5D39F578C9363BC04B16F08514EB7CC9980D7609F55558991A0BFE73502D316792BB11154B0E1F6A90ABAAC1038007D8841275FA565A40C97B5A3DAC889365A47C2A5826B93A6A00565800726E9454A4D604A4B72CBCE8C404BE22F6FE26E11D303ED1758E4C588CFB37528988DEEB76789A19797E0C2F3E7B07ED76CF8EC2E9DA2D11980BCF7256A8A5751C664F39610BC0182DD584B5DF9B8423A521B47B01B016B093D8E7AE5344F2FDDFA548B83C0E71D0C76EDC11D6204266015AEC7BCCAE9257780E2C79B320362913FF601FD152B0D28EA5E6AB47B17AD9CE4863879FA2CADCB1160B1B652F772954639AA38D2E74065616B5DB94AD5451808D8BD0C5A148581E3B4F1B6436FF9022E91ED91901005E236496ED1723314EFFE0349C8513DB2A79C7C197F0596E6F92D27AD29833AB62F7A51EB55726F8230D0C97072BB9CFF81D664711366EA02266D739417CD65E48FBC5E150BC8226DC0A029E0F3CBB6527A35AF2A26DC939CCA8A9E940A41F3A5CEF6E55CABC8D3BA78B7A504A99A43F46562A0B110F7D7CF553DAB3120198240A2256E37BDD7C3EF6E12F490B63302BF0D485CFF6A44BA225B3ACAC3791D2A7D232BF5CEAA75BA34003283DE044011DDF39426576EF4BB42BDD4EB1BC50713DDEDA1810A7F508BFE68D1CD328BBF98E402AF99B01CB8ED9811039DB8D0B9D4C13C20A00A2867DF3041C68459C5E54C212599998E15E638B0607D29C5638DF44AB1D00D17EE3D181338E319685D844C9BF78D759463251F9AC43DDBF9E1725FB4082E03D94B72D5C10BD89F051EF7223579AF8EBDBE6C56C8709AC5FE0AB4ADE4329382CA866407566DF05C1AB4C806CDB78C81A20D81675C861DAC3AE9FFEC94FA6F905031525139CE31D742F1AA69B547D4A282E23AAE413F0155D1DAD9C1370D47DA1DC71B7971BD6A962635A794BADD5885C129361FC1878FD03846601F36CF3A950AFB959B5347AB32A6483C9B49ED24588B5406A96524929CD293939168873E771898B65CBE8EF9044C05793F666706DD1FD939E395A9C0B2E3949CB6F2561CBD8E33F31304BE18C793FB4CAA561EF99F206864899B483C58D7FA06EEDDE9F76ED5A3C6532D98165776921A03D0A5DA3D5B7A017140CA3A684E5647610C34B6752EF66C16E75E459136A6FA9E2E1C5CCA3E5DE74C0B03BA48C4A4ED8BAFBE6AD4313D905D73B8C3596AA706D5A9FE81EDE67C1F83776DFC38DB09D287E8BBBBD082F5D1F17B48808E44F51C377696939AA6660324F1CA9233FBEC40DC4FB9BC9E99C83C18EA662DA1F1BA5FA2AC533E3507531BF8AEFC54147CF268E08DBB479058F262E4BC94887751B0E50613F25095BED61FB827C6DBE06DF7BB863263C6E09BFB04A47A1CF78D469A4D8C8135333FB9E459747B8FBF19B3D8390AF04572BB536160569EB00DC94CDDE73813497504581483FC20079ED900C688194ADEC48C67F2E6967D2A562A88D665394FC2D70546D3EBA6BF8517D1630A37B46EE214D7BA4690F0765BB6B0E48FC9A3E4DD50F833A844BE40B0C3153451116809A11243C350C1C4C514CF4FC897ACF17D8DF6FE63F76E9EB67F89C594AFF932D70112422CCEF5BBA53961FAAA52FA36C5552072FBA275CAAA66B8D639345184985ABBAC72C2FBD76E5E14E4D3FA0746ED7BCE8A22B4DCAD62183F2E3518A9DEAA4D52C7FB0D88E55DEF831101DA4C1BA74080C4849933368DAE5523795FED95A665368F031539DA890F4F4E6EB3471A19418E8308A710193CB8D657A9247D246222510C43855E46D9C9F740CDE66366DE4D1512B64CB8011893662A4A5517B07F091C1DCD1FF7DD11661AFC89377D4A1A518AA60D65FA5B8CA484B8F3C2E9137708D5CE98B4F8EC005A206B817C92C7AAA2BF4282327A0FA6B2B617D612B20B664CE4025A81693F13FC1A59B73C913062930224D5F9D6EE84ED6A9EB7975BE7E42B6C347A88390B9B9AB6C452BAB98BA1538882FCB9BEEC5737E2328590D9117386200818351C99E116D9C0FFD6D0BE2FA7A605F8E9DCBB2DB05CB6261D5BA897CD3C5CE4EDCCA2072867030FB01055A56A6B02D84038353ADCA427B46819A28AA374873F185AF61E8B23D78646A663FA16078B51CEC6AEA0A74EBFE071AD90EB4408D4C1B2B29149BAFEFDDB46D3A772A79DFC0B006B2344BD2C53FBDF787C04BAC5C357900CBBE2E383A7D378C524D8C5D1438558E89A9F0B23A543E50385EE6B91C9657AC1273AB27CCAC5AF84F8DEBFE0C363088285F60174B2E4DCF31E52C1ED1ADEE538BE0C761F6320E18036AA6018D2AA7B8A4BE56784624EDD40A38267BDDDE5F5B56F00178673ED5CAE86201C6A0C1E5907484B4CB4E4B1FD8AF6B70C1A84FF3437262396AA4782A23113C0B05193F25B9BD62BA6527616D54B0A05AD8217884B8DCE2C3EEEDEBE55EE6096FEA3F86F3476AA6530C6283DD47E63479FF70F3017C990F93A57A20B3E2EE90EDB0C0EBAA40AA7A187C4215FFA64A6788F548A2DC104E95C2F0461BA536AC5F1B9641F5B7BB9F59516A48035E88BD5B7901B8C6317DBC9DAC3374486086CC63456517A9DD5BF16A9CFE033E2E701A6C203AD82F7039ADE7E18FC5A45D8173A076A788F233034C8BDBD3571714A8E5EE962168C2DDF8ABE786D8E16A9A20502661537711E1E49F51C093AAE052BEAE18456E5D9FADB4771F6553ABF54BB8639E4CEDE68DCFCB4DE945903CBCCCF2897BD3374648F9A87714C983FF6180AA98387426BDA595BA83F17A3967918457DECB07F72471E029254122CF6A0C89350FE6FA5B0D16504930FA0DFF8E1AED02817DEE6BAFE9F788AEEB774007512396CBEBCC782E03F9FC5646C070C05AE0D3B237081FF5604AB5AD1F475FE214227CE67C3A890E2C5E58DB8EA7CD2CF6979DA3C81D0B805EE3ED003A70F2580E1CA04E8081C2026306F54378E29FF8D2E4243CCB414C34F1453EB12F9BDD87C76B68B013C11DB019902F97A8154D691327142AA61CECF2B8123C1C9F60F6228C6ABA057092452CB3F5CF5569453767D76564114125C19CCF850BFE6AE3B6AC8B824E867D29EEFEC97BA5B1C44BAEA82A68FCBE74505A7775F548FB0EC369DE103B43ADE14802AC8D868AE6FBEA64E4416EE322924AE0129B675B17013F5399D59DA51317A6837284E5DA189A0DCD43D99338D7CAF01C4EF729B087D15E55B9443A8A201D3AAE2B26BBD8E762ECE3F7F7A843A1E211A78360A33161958FFC7E425810BF40BA68133E9A9CD761A02FD937F5BFAF9A11225DCF50E4279843AC1354CAD799F3F5E4859FF208A9FE0B4F88FE625662058C1812F2F70EB87681DE0782EE3F77E16BB557F0BAE2AA50218C0909C43DBBC32F1B06ACB2E4F8A78B7E1647AF8CF79DB67D4C3D4A7B78C8B000623B3151644CB9A698E4A96DF35DAE30C98BBBF06782AB88058D5FAC5A9A7A7FA81251A894754E5141585D4BF6E0D5D793FF4F2A5F36A8C50DB84AFE75E7FA33938143B3B20A2E7D836CC0B8967C2AAC0B5C49FD2987D7ACE18B439AF385F613F0A4433C270D13D587FB5111DF5EEB42C16EC6A128409BBC0FF71DAA2053300C4CC3B78922BA4BAE4A5C8C85C4231C36147152F0FE11795BBED1D5E61C28C73ED5AE8F58EE0C7701BDE5F8FAB3A82936A3DB8F85946C107C0C3E30D1D495EC5C843D56AADA1B894B7F51A56A6FE8787138A38BCDFF221DA5A6CD25EDE2F31B5DD5F85A121919D58A9F63B82F88B5151FCE566ECF15D07A93BCE940F1CD0D49FBAD44671A7A45F1B1EC217EC44D4F5F8FBA06367177581EE93A25FE7DCC84EFAA890BB27DCDC2851AE574B5AAB386D9A869435AA1BCD047817F1020F3BA1778ECDC373EFD2F8FCBB749942DF8C6B694AC7C7FA45D74E46F1E93C4D5A032607877F7A81F65F873200DAD8BDDF332C8DA2C34ECB2B7448BBDACBC5C5A0F48569E9C423697DB850E45D59BEACF9919A468A45A61D41D487D07C32C400810139CCE2E541B62BEB21BDA57F4DC2B39227B109936BDBAF6BD28DDD236A48A45F2286C730C66995E556CF2CADB9F2AA5CF1186E0C1F0183EC3F821EA6947FD98D2BD7E25ACAD0BEA9609B96EE6FFDAA85535713686DA3FB8DF191326CABA0B329E50AA93ABC60D3E0437474AE798D507142AD7BBD384AC18991F3C270E833B0EF75FEB3C9106C1F7A73A7A87CC4A48D1BD757A5F9DC47913E90679918A6C3791ED893F3D1F8489505F605F49EC6F80CF2364C47E78C1D976A24E63F2988DE44C1639E8A36FEC2CEBED621891DB4AC8C5705C6DB64B96CC203072A6C9FCFF2FF0E75BBB75C0BA4073B583BF7E5EF4204CD10F5FB3F82A44EA41EC9D083FA259A47B0D782B7F1829BFCE449C0933E3AB484051CEAE87B74DC76EFBC64825087F12A65CAA842BF2AAFE0B1FDA0AA96061E0D3FCE55C34CFA453C7E9A670870198A7E0C335A112FA3BDD33AB31F0062899B2A6ADFDD83DF22E9085D5D4500BC380144F4A47EA9ECC81894E1D9EEFB0B1FB06DE079E048E915954FB1A1BB51F245E604A4A968EAB4995CB216DDE395FD7665F121BFC8541C6C8C215B5072BF41FC267D69F4CB5B5C713E7E2761FED8BE77A34F2ED17C03DD4AC3018420E62B9AF370FB365C0874F711806A11667D28870EC7B511D2180DD88EAB9214276C5AB8920751532602269B9236AE6AF1E2CEE27BAC43DF807951D6CC61AE3E392CED46011DA83310A50AA8BBAC48E59570CE2C91A45ED128DE21F7923B07424191FAEF9D65EFBE991CD7684D929440E0BBE253EC54372ECBE3783CA2508B689A40E37D956ED2C44F551351B3C0625176DAAE576E4E0D67C9C6713AC82BE5BA6A08A7FDBB8C2CA3AE3ACD8E752C45C2AE7C1E8BF2FE4504870015B4BFB668E304472D2AA7704AE83D93546F814DCCD544C4743519A90EA04144482F7AA8AE7AFEB1BC24CDDCF324F44F5D68A24469B4B57EA9ED0D1D959E3623E3C826DC9E46CCBA0679014EB1EF7CD3B4138356E440514AFF8742F2C4F6B0F44E97472C6007C290FF976CF38241236EAFFEE9792ED4EB6B968A201E4EB6CD4C81227C02606420EEF942667F822112AB731D5B43678C54DC53DA8B6BA0DFD6880890F4392966FC5B0D38C3EFEF451FE51DE8A89673AE0C1D08314F49DEFBEBDAE5D7E0F96A1E0DD531A976938444465392ADDF254160B3ED4606DB2F877E3E891F04E1D6C537FBA7EF98F27E87B0DA629E6FDCF2DD6FD6994A4506B8B559B18B1F9AD5B0E73A663CE0D735E1F68875C2AB0657C74728686718CFA103C628215C00B29ED059DE44F8645734BD62B253275D1214825B41BD57775F3A06729F5056F9A0E533FAE2DAD9E1D8BA5C9C728E197C465119703F1B8A366CDF69D8B116CE2A390B1252496C29D8A160B96DD283D989FDB68C40AE74E719995E4B653ECA9CD6D02ACE94A501C718F1ADCF7965EC8F35F65461EAB7F3C8EC9653D57DAA0FD1C43A3D7D018316C1480A9D691017532AF8846032C20F4C2400221C8FF1E2861E166572F7BDFECB4E1A048AE64C02C07441AAC190CCD8F44262AC6A72D9A9E1D8CD1EA94A2039CDFEF48409FDBDC21425299451B4D48556A45E7130D68097D659EAF5905E0BD39E889911AA75D0C0C91D7AA25EC0895F810E2A5F95A1227C9844BCF2B8F7D83E0D96F4785F76150DF592DC247FF5CDA1DB8A1D9621C32B7A3594BF68906818EE6F38AFF5B5E44DB5623AB3059AA9C46AAEE49AC3D77CDA5BF710ADF3E6B0B044F46511D3E943CDA49578C1D44D2BEC0C313C0359860623BF910D8FBDC66F1EB08AB68681C5CFCC979D131C78E422B71318CF453E04DAF2E7FD4A53EEA96D641E5DE3D8550D4B5FE68C49798DD40E80758EC75E3E526914465EEF3D3791F9FB1F01225F98DFD4ACC4B3F4F168663DF67BB285BD4CEFD4418D5C09EE4B5627A41FEF3850E475DBE752A332BDE07E1A7771A6003C8566C48E131F8CEE28EDC158F00368E8B8AFB88E3F5CAACE0739BA7AC5C168E630DE91B4988F15D3C9F6CFDB27B6C0A2AC4A58828E699D97B8B3F0082EE93396E7DE0844E863A074CAC9630D67EA80F213E028BD15CD44B6A3152674FB07A3D114FB95C8FEA9A14CB4B9EF3BB18FDA4F006738451BB5DBAE3E8671C944F4921E127892D35CC8C0C0036BF1B94533D794F31E506422C0D73F53732F5296C568441C95D0E841A49039CCE39FADFEC7C00CD80CBED54E167C6DFB677F05A06412A245444298D93131105D5BBC858D9BFB11C3168AC7E581C34B55705829793B31420D8D3C6C2EB475EFD7AEE3D2F2DFA9186AC5A4929A07FD57D36D261C0C051771160BB61933695BD34F8ED251EB98EA707092464D7305C4E36BC65EF947C0836BFFA0EDBF745576154D3D76A3602E6169176A6AE1340522BDF66A228BF868CD8C72ED18FE9C26D941755A1E8988E40D1F964E13722071D8C230747D96A6FED61798F3E6CA11E781D6136BE74C837CB636AAB130A18B78C67DD2C9304D8761E2FD47F46015E569B231641B5D6D9F4F2A0AB0C24941D0C2FADC790978EBCDB11BE6080D0220696A8813D2820E0BE02B79A887325C06DB5EFA7F71ECB1DEAA1A17A850A63C11615F052D500B6E02BDA566D577C061AB833034F3CBBCC2C68CADB47B4223005A61237A61A788C48202D568468AB0CCB98A2C25C55B971DC5EE346723B243E7A9A47A141DABCD270F4E5486339472BF41B1A0D5D6F9897B2E19820D9AEDA994F25D3443C06D1C1663E26F736FD4B04337BD2AAAF8ACC595BACF76A94BABD81D3E39C88F7302726A7F34842FCA8458DB92227A551143D08ABB570F4BFED34DF1AF82CC9AEF7B4B0955FA5862E45343B9C03FD32378825842A429507B7F35A291A548CCAB8B83FEFFB68DDCDE687FE405DB77D37D97521A0DCD842907308EB45E450A91B4ED140EFBC968DB04F917CD8DB5447BA7BD1119EE3C8B769ACD345551E3C99DBCA6B31B57A023A7E93D2783567340AF0F7E4CDB263ACBE0A625FA573DE18523BB441DBFD80B60F22D954C1AC9B0C8573F02D5824634FDAABF46E5253F897270971376873469E187AF9540E4511947864C5D4A5534C884D35821358253DB36A82DA6D5210576483990E3B61790C0FB2C87F4073DBBF37D040BDDA114214F6D2864BB8653317E59F803F54346400F4513BC1D05F0D4F33F030413EFAABB83FA407D0F19A3C6E3F5077E2CD7C5C133171655DAB8FBB504AD526F15073F7B5A3DE73E446794D7E16759A00F31F6343A55DD4D3466FF78E3577EFBCFA66928EE22259620B257C4BF122E1EE5EF797194B2A0FC2F9B3405F3B5A0945B026C0B8BC354EBA34E7895E7530657BA991B802C1125773D624B9ECE913ABED0C41116A1F0AFD0F08A9A58891A1CB9BF0100FB5A272C5F94CB6879283C496EB2766AA82CA7302230A2A2D7DBACD8C6210E7B2679AE87365F32E8E76E4C9A902A08B5E685F4DC0AE6FDB414151965289A64AD7D8A7262A4B37818D9719BF373C93E72BB0A5451BB50CB1E958D0D47D77A310A161F6912934DB56BCE9B77ABECF96381008BE615472F42C86ACEC62D27EAD5B4321F77491604A47ACCA662606F12E1ABC7EDF6CAAD388E7E4D9997FFBB5BA36A8319494B315629B0E0475B8A9FCB0EE47783A49B503D45555E660D7944662ECBC9106197415922376C3A09CB31E21229D4CF8CBA3B5D0B3243076C8D95923886DF594F31C1040DE464789E60E822D27732B56DD16C259D333FED7469E1D3CBE52BC1EA2CB8F47FC22900F91C8D7EA24965AB6A336A870A38D1FA8C947FE0001084B71DC5DD73A766AEB99644CE537A9A6F6A198C0D407C868B7D8230DB17247352261679D3A14E01368F03EB42D870A46FBC0E3F05FBFE54EEC0B320AFA34867992447881CDF410B1690BDD8DD1F96026F73EFEFB0C5B066C04B718E8F1D1922F38AA8925BE75D4F82AB869CC5A557E4B756082645DE00EF340DB0BAE4CAF6CF06AD6EBF7D08656B20A6F4C262F14922DB8767828997EB07C8976CAA6A2BECBDE7D5E0FA2BC73D572E7F8BE6003176C643199BBCEE91B4C703E4B5116204F97954B70292C7E81EADF7B33B9032ED60D4A5FB3102FBF1B868360389C53CC0CCF4DAE24E3C2EFDA91588B7A902A336C5BBFEE3195A1F6F6C5302392B3BDD1E1B257019A71AFB9967288CFD085160A29FBA43CEF81B4F778BC753D13E64BDD3374EDB5E5D4C8EC4F8FA0AA172E023474F1662C82301F56FE2820D9CE0747A08ABB5D37A8026080F7F258927047B182B38C2BD162B48722A64D5C9AB30B2E5011E8934031B2A7E31724C80B9A9C664EA14E75428F5DC7D8782FB816669B73020A550D295322F1A8CCF82F6ADE70C267E6522EB6A7BC8CF27FF932D6E899CC9AAE7BFFF983A2FCFCDDC8D6B9D6EE4AB13EA0A28D3F9AADC9FEB7DE0312FCA4F676E08A5BB14E42A7FEA1131902AC6D66CB20312E087594F9CA2CF4E1C4FA051BFFB79E61A84F8E9816682BB440BEFE037DA42DFC4945D3E5CE1F9931DCF304DC31786C4DD8DEA65527B2850F1B3D94884EB6001F4A851FE6BC7F27FFDCAA2C919B359BF83BA55CB50D9A501E34ED1101DDD0207824D242B3DB8D23BE5A39744AEB9EBD3DB7C60DC78B6CE9138E3A5AA3E4B4A73B3C901FBCFD55708ED878DD96AB989DCE987B9A4E11A632235C350FA85D0B78301A982C80BFB22D666A821FA4FF54148707D71C8F810F45D538C447A019917E8674A7ABF2BF76D962AF27F41FFCC7E58EBA28E06DDE7A094DBD6A48F370244EAC7C570353A742FE6EC6F307AD38B6B753E5FECCD2551083593114B706A2FFE5DC401303FF336552B90E1518231E4213E56F3D5A993F7BA3A5F86A27232638E6EBEE6A132248419A881AB4E1ED08D43E317A97FDDF2ADBD898F15DD4FE1D46AC4748721A73ABDD97C456FA6F4C34DE47DEBF2797AE4872B726E8CC25D4F3E54BB01B1BEFE09716F470D021C1C1BD3931AE00A91E254E26E58A03DFE5A2E3EE6717E2610F71A1E7A664F435A6EFFA92C54C4B09BD260D675146B553AA3B5623765CB3522ADC14D5FE61AE2853C3273ED621A4424D0648E601A846C32507EE730BE8E4F947449426409B14D11800B296111979C17CA94348C766F8C163FC305092D6C6F2CD9566352878856FF6BFE7C13EEAF6AE14E94F1C6935ADCCD1B942652675AA9AB88F3CC8445FD231AC2BACE370D9BCCBC644652F4DA4F02640957B8DA6F1D3F0702DB13A33E36925F973D6124C95FD4EE16B4D8222247252A3C26260053FD9901180236B3B379A867C9187ABD352CEC8FA99E25CF2F93AD8BC45A26F98AF8A699C42C639DDFF8669C3A55A998B19F9D50AC72542B1B1E67F18C2C2F2DD7AFF3BB5CC967257FFD651F93F88A6FD537F5D929C45821386D82DD97342B9C4C5DED390E070F984640631B6696657433536D984C20F97D1386266424D439B60D8F5EF8807F72E6467A44603516F5AC72171C177AE24ECCFF2C47FECC356B31CB991D373CD98443AC60B79A8604332FFD3C294DD52E497CB249959851AB401B1D93FB4E28B0C47082E0643133508598DFCA1E08BA7B01E22B03CA76AF36CC51CFD5240E27F5BB01BF0C43F3FB83DA385AF806545EB18D4E6844BC5D15BECA3EC09E83089B66181C9428C23EEE4D597EEFBB2F75200A9F48C15B858B972CE6083EC913F87E2BDF91F3657C87E8DA6AB5F0C7BF8E033F285A8089FB287CD46D4958C2373C022EB29445F4689328A3AB49CFA43379069E1D694CB6844773882AE3F8F5281FE830ADD377170F31B93B9817F3BD4F2A18F7C6DDB88E41545F9E97684A11A45DCA38EB2F909C867E79000A1856183BAE01DC1E56BBD8D720052E1CF6AD3E86E170BE132DA5EDCB0EB4B5EACFA9B40FDCF034889C580577AC261084FDECB3EDE0FB3442CD75FF9C1363966642891FC3C2397EF97451966EC871328C2A64831E813C511B9CDB050099700625A333C40590E5A7BE0DEE0834D07311B73AA83A60A9B8C11917E1834914DA917BFB40B61F3D492447398C8D1EC5599FB5FA37DF6CA38ED89090CB31634EB438D37D760DFBCD52227B702B067D3CD44682D70C6E4DDAE5123B4FF2E9889508FDEC1EA51E88317DDB12933C2B7328A5C502A36CD3A8F37F172BD1C68B3FEDAF05B81396311C619BA6956703C8D86E86FDD8E1EB5094E73AA1C40AE0A5D10D17FBB618A98929EA5FF7F05A896EB851CCDC989FE0333052F4D1C2FE009E4051C9AD0D775DC33D39072B18397A2AE3AE33BB00EF50133F0A71CDFE3F9181D2374ADBFABDAF130D5B51D5FE18A530DD7DB2DCBB593DCA43C0D21501A1AEA24770F36A140F6B40BEB4FB9F934350C24E3AB9CD94CBD9A3DED61C880A57EE8AE5E01809D90558750FFCF83A0116D37229F61E986FD524A8B306F61DFD3F4B19214D35F81E19D06C38E5BE3A7A7CDE3F3EC89137367080BD5962FE79C88C023E1D2156251777472B08E66586973205EEC1856837EC0AAF5C00DBCE8B4088D95EB1659676C947D8BE14E811F11A7A964C070FCB38F8494E69CE4D7EABC6F99E3DE707DA4FC7D2B684E4B7619DB0D0C63C26DB135695BE4CB3C1E25782B730EABD689EDC8D58DB3ECF3F9398F652DBDE4BC14F45B5F17200A3934154F1E7D41C0F07B615F468C2692FDE948C95AD06B4FA7FE934216EB632001E3CDD6C52A90A12D7FC5A525FF86DE0E1E0857FEEF892C0927D5E04F3A5D24A5507522F0546E4F9CAC0ADA5CD0D7C6D634912A5AFB05835068DAC6F68B6E90B3E5E3698B13D541C8F26754BEA8D41FE4A081A9D545B1F63D022935B422AC4AC57D1B99191F56AB4E7B59847FE0ED96F0014034FA151BF2D9D31A4426461A97173FEA8B4C297D7261A4B0DE8D1933E6D2B6251EC1F822EBC3C4BC76203BCDF964ADC0AB48CC200271649F8105143923B59230229922244F7928A2F979F88486E5108420FA33B01321EB7EC7C1288C55AB531888A4DFF38B9F8DCBCC82876DAFE9A6B9BEC0F55F1A153184354854FD06CC4DBEE8DA64C2E3C6AC2097F7C38E08E65CD7543B1727FC81E58B824FAE9A49E30B5544CEFA10B9328F9C68D9D621A60C8FEEB894E9F8AE5D07EE9DEB906AAA8F9DA5C9F3B47AC98AEDA58AB01B806559165577D9E14DE350150A23B126A51358C03F524C4224E552D62E5DEA71AB5312165B679975438B5CEBA0B285A7D48B1857A487BD94BC1ADFBFE283F94699273D6B8B2F827B670A6BA7DA21A5BCA3B8E83E5123F304C67AD27BFD804C4FB3CA8E38F369426AFCA30F7E2B8426525F22E9AE073FEA68BC842E59BA1C2B55366158E45376D266F798B3FF7CCD0486872C1889CD16911268E334253908252C25EA40EA70F603C2B33A9019EC556678A5BEFDF2E48742D304DA2AD04BDDE226B42653A0531BA4CA768AB2E0CC681DC806759095722E409AFE18405C624BC2B0D38164FE728467D3247FD03FD5FEBAA9CDFFB97CACCAC33DDBF335033C721B624CC91E7632BD83415492D84F8DC8CA19886A58A15E4BA44A7AC63B8097746041220C81A9474786555D5D802945A676E443E076AF13A596678C61555E29502A82A8D21F18CF2845BEDC295C450E8017F35D51CAC87C3E2163A6BDF7F7FF08D22CC60E59E09A9FC8318F848FCADB44DD76BA20B0B1840257B2391458851B332AA373DE2B4A6CEC1F3B1D7C97B3362C4F61CF575F70B252234BAF629C18B9114D34718C6623374A849EAD9C4F302D75987AB14E2309D0A19D896F167E9C44357D6FFFD9B5474B53D704CE03389AE8943FB409480D22564637A88EE12AFA1D86C35534DC26B55851FFC597B5662700CBC3FBB98107E3217942FA0AB047BBBD8CC54B3DCC5FC3FD406FCAC55F1B5160F4981ADE52DA07B1F6A4C5B5D202277FCA27870082FBC8BD87DCA74E63A943F796FB45171BF50469B69902D4FC6B5232D681000AC8BF11C7AFBBC1E5BCC7FF185E7173D9AD4B1ED9857B6F09956B9FDD1251E266175F11FBFB88B683C7865C222F018C100E1C22EA6DF52016D06002E7B657F4A7A345524376C82570F3E3AB0B32B600F5B8DA7B83ABE0B043AFBA511E4FCAC5D4F2B89E9679151AC9698D2AB252426C5F4C6489985E402A36D8C952F4834857F56D6B860989B4957CB95ADA66C52E37CD9F0C5266E70971DFE2B34CEC51A9C3AEE5B95D7669102AA8A86D072D884BA4FEBDD63FAEE0B88ACC38DD8A7F1C6CA700ADA4D822FA95BD289D27CE9706E46EE48760DB040D260509F76E22E8758D47C6F1410D1AFAD24350070B92DAB5B0C451A6133DFA1888CD2A30D2C2AF8360A8991F81D1DD6939A3D11EE5BA1A68A72F8817541158BDD3CFBEC1ADB2D3653D237B3C788CFEA1271DC74A5E403F49B58CB664D855D6D0810A96B81751F5C343D3D7EB68A69BDD14EFCC5DD7F83BF320B1E28B348A8FC3584625893FEE8704AF4FBB95898318A02151D044DC32308ECBACF9E68E213DA88FECC23DE5095FD0FE0DF82AB5252316C46EDE411BA7B65F9A6DC1296443C30AFB22250D06885345FC8C8A961FF733B3F93D43D42531F46645412D9C3124B94C56ADA05072940A5C50E7E5EA767CF8CF748F938E55BCF6BBE042256557E09D38ACFF5100FCDDB467CBD36B5AE9BFA3E17BDFA111FA7C895F8E14BDA67F2EF494C88038A902E6893271FF7EE10228A37EE67D6B2466C9D0C9C2D23D596DC331789406C5964935B3971CD8412AEC91F882A89A3ADFC0DE295BD582C6F84408FBDA0FBFE34BA7975585E3E092D628DDFA2B21B879D664974D1F86FF45E03A112126F4F45A39DC60183D6319F7E826A4518246E0D1039B4C74ED10B6CD97B727EA0C3AA6798DAA4836E59A5E2B0AAD4BBDC3028BFF363CDA370C3C7F9E806506702BBBFFD896A4289A01C2D51A13DBA59D69B64C72B9A76216E9BA36DEAECC235091168EE432B5B2E031EFB293F454DAF12D213D898BBE98D3DA3CF8C0B6C9272C68369CE8604D1930254CFE5BDF7D89EEA439A2C0A4459CE64E6D4269270C853CF2466F37861A5FF3346E3E2660F78BFB87579901C02E3F8B3110945A4E3FA1C98676A19870BDC6FBB1EE47C56C44C0FEFF734DF52ADBBA07448BCDA24FC7AE944E3181B374E3A2CF6D49B7241BF54EDE6EB0115EBF516CC8E66D31E07575CB36CD1BB49E635431064978E8741CE1AB9BA69F830894AE81DC3BCE7F4B37D40E88ADD7B60E068FC1D8A1D40B63B2DF217D914663DE4188ABE7DB5EC33A3458387812B308BC6DE0084BF490B06FDEFCFB9C81BE87557F031869A92628B3459DDE9F890EF3B4170FB218579A6AFE212D76C17CEC3018A82963AB58FA59A923426F067FB91FE8E85CDDE6CFDB43488071D13538070DDBA4F0BA572B05CEA0A2EAD09E011864B2E4EF434C42F565B6672F717EF87870E6A53C3C6C3FED31C53E5B79FB0A85E478E8755C5C044AE2A033C69272AC6C9A4D7B7F4DF9CA71681CA2770C59ECB90BE9AA36F9BA485DED0273E59F8F9DC64A8DC7B0F1C755B88D12B8C94D34A521E20BE04C62CE8BF1FDEBDFC92FF1EEF8D24C1064970355FD38F2C3A8AF557B65E4ADE2ACE274E64A081CB254A86F89E22EE8B0A454979A6C424F0A3C852019A2153A21057ED067D4BFB3F93111DD335E092553BA02C896146F4FE6E18330791BAC3C429594ACBF83552B441895308EC47B4434156C502A3820F91B180F25C67C582B1925275AB7F0318522CA7A7D0286844A065E7AAAE23EA1A8435BE80E27DE9DD97D3600D215561E7E828A5007240AF37D801BB2AD51928C0601F75398DA5D47D45A1A12E0B202D258A9D33F0889A0E1366FB00E2779581B09D588136400DF94AC9889C7E8FEB23B58DBBEA15DE72E430E1622471BE6F58B78C3908F17A5B4553389BBD1CF2CE3293830731674F4BB3C0D0B334A3D6F42338CDC10E67B97E75C8073B192DC8E786757D060DAEF712B976DC2AEBD2CF664A2F991C1A2BD25CD89AB8E84B4939FDBCB25B4546689D7D80A5C846CEEB75BB41FA105F9834AEF5692E157999CAB73F8688BE98A1B45BF0D620666C4096FC5BEBA21E35D887C53D0BDAD7EFE19BB98F43651B432241607ECA368A657DAE0F5ECCAABC7E04C44A34FF2E4B72E5F1A123A75E5A7A3F947AFEE7EC6DA8B827ED3AABCB7A614943150501A86D3814A9385B425B832E551CD4FCC4099C551D7B2A0DB72D70A27CB40E92F5353B61318D8AC4005EE8133BA7BF250070DCFF597BE449D5487FA542AFDF60B2DB608B2CA42C855ED636586BB17858FF11BEA84DEE904460B6E204812596133180F4674AC84DE9ED459CEC1D4002316263A2689BD42A8804CD12CBE7A42C3B1AE7E398009C264EFAE0B95EB09C6F9D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
+            Assert.True(Arrays.AreEqual(expected, attachedSig));
+
+            signer.Init(false, pubParams);
+
+            Assert.True(signer.VerifySignature(msg, sig));
+        }
+
+        private static string[] splitOn(string input, char c)
+        {
+            string s = input.Trim();
+            ArrayList l = new ArrayList();
+
+            int idx = s.IndexOf(c);
+            while (idx > 0)
+            {
+                l.Add(s.Substring(0, idx));
+                s = s.Substring(idx + 1).Trim();
+                idx = s.IndexOf(c);
+            }
+
+            if (s.Length > 0)
+            {
+                l.Add(s);
+            }
+              
+            return l.ToArray(typeof(string)) as string[];
+        }
+    }
+}
\ No newline at end of file